Jump to content

Space Map with Warp Cell Calculator


RONinja

Recommended Posts

----Edits

09/27      Changed math from U.S. tons to tonnes.  907.185 -> 1000
10/07      Updated svg bootstrap and ADDED MasterOfAll's Newer Version.


NEW VERSION MADE BY
MasterOfAll  
dualuniverse_2020-10-07t21h05m53s.png
Features
Time till Destination   = TTD
Displays Destination that was chosen.
Does Speed displayed as Velocity
Comes with Sleek White Buttons.

Pasteable Form Here Just Paste to PB and Link Screen and Core.

{"slots":{"0":{"name":"screen","type":{"events":[],"methods":[]}},"1":{"name":"core","type":{"events":[],"methods":[]}},"2":{"name":"slot3","type":{"events":[],"methods":[]}},"3":{"name":"slot4","type":{"events":[],"methods":[]}},"4":{"name":"slot5","type":{"events":[],"methods":[]}},"5":{"name":"slot6","type":{"events":[],"methods":[]}},"6":{"name":"slot7","type":{"events":[],"methods":[]}},"7":{"name":"slot8","type":{"events":[],"methods":[]}},"8":{"name":"slot9","type":{"events":[],"methods":[]}},"9":{"name":"slot10","type":{"events":[],"methods":[]}},"-1":{"name":"unit","type":{"events":[],"methods":[]}},"-2":{"name":"system","type":{"events":[],"methods":[]}},"-3":{"name":"library","type":{"events":[],"methods":[]}}},"handlers":[{"code":"MapScreenMouseX = x\nMapScreenMouseY = y\nMapScreenMouseDown = false\nlocal buttonNo = evaluateButtons()\nif MapScreenButtonSelected > 0 and MapScreenButtonSelected == buttonNo then\n    onButtonUp(buttonNo)\n    onClick(buttonNo)\nend\nMapScreenButtonSelected = -buttonNo","filter":{"args":[{"variable":"*"},{"variable":"*"}],"signature":"mouseUp(x,y)","slotKey":"0"},"key":"0"},{"code":"MapScreenMouseX = x\nMapScreenMouseY = y\nMapScreenMouseDown = true\nMapScreenButtonSelected = evaluateButtons()\nonButtonDown(MapScreenButtonSelected)\n","filter":{"args":[{"variable":"*"},{"variable":"*"}],"signature":"mouseDown(x,y)","slotKey":"0"},"key":"1"},{"code":"function Atlas()\n        return {\n    [0] = {\n      [1]={\n        GM=6930729684,\n        bodyId=1,\n        center={x=17465536.000,y=22665536.000,z=-34464.000},\n        name='Madis',\n        planetarySystemId=0,\n        radius=44300\n      },\n      [2]={\n        GM=157470826617,\n        bodyId=2,\n        center={x=-8.000,y=-8.000,z=-126303.000},\n        name='Alioth',\n        planetarySystemId=0,\n        radius=126068\n      },\n      [3]={\n        GM=11776905000,\n        bodyId=3,\n        center={x=29165536.000,y=10865536.000,z=65536.000},\n        name='Thades',\n        planetarySystemId=0,\n        radius=49000\n      },\n      [4]={\n        GM=14893847582,\n        bodyId=4,\n        center={x=-13234464.000,y=55765536.000,z=465536.000},\n        name='Talemai',\n        planetarySystemId=0,\n        radius=57450\n      },\n      [5]={\n        GM=16951680000,\n        bodyId=5,\n        center={x=-43534464.000,y=22565536.000,z=-48934464.000},\n        name='Feli',\n        planetarySystemId=0,\n        radius=60000\n      },\n      [6]={\n        GM=10502547741,\n        bodyId=6,\n        center={x=52765536.000,y=27165538.000,z=52065535.000},\n        name='Sicari',\n        planetarySystemId=0,\n        radius=51100\n      },\n      [7]={\n        GM=13033380591,\n        bodyId=7,\n        center={x=58665538.000,y=29665535.000,z=58165535.000},\n        name='Sinnen',\n        planetarySystemId=0,\n        radius=54950\n      },\n      [8]={\n        GM=18477723600,\n        bodyId=8,\n        center={x=80865538.000,y=54665536.000,z=-934463.940},\n        name='Teoma',\n        planetarySystemId=0,\n        radius=62000\n      },\n      [9]={\n        GM=18606274330,\n        bodyId=9,\n        center={x=-94134462.000,y=12765534.000,z=-3634464.000},\n        name='Jago',\n        planetarySystemId=0,\n        radius=61590\n      },\n      [10]={\n        GM=78480000,\n        bodyId=10,\n        center={x=17448118.224,y=22966846.286,z=143078.820},\n        name='Madis Moon 1',\n        planetarySystemId=0,\n        radius=10000\n      },\n      [11]={\n        GM=237402000,\n        bodyId=11,\n        center={x=17194626.000,y=22243633.880,z=-214962.810},\n        name='Madis Moon 2',\n        planetarySystemId=0,\n        radius=11000\n      },\n      [12]={\n        GM=265046609,\n        bodyId=12,\n        center={x=17520614.000,y=22184730.000,z=-309989.990},\n        name='Madis Moon 3',\n        planetarySystemId=0,\n        radius=15005\n      },\n      [21]={\n        GM=2118960000,\n        bodyId=21,\n        center={x=457933.000,y=-1509011.000,z=115524.000},\n        name='Alioth Moon 1',\n        planetarySystemId=0,\n        radius=30000\n      },\n      [22]={\n        GM=2165833514,\n        bodyId=22,\n        center={x=-1692694.000,y=729681.000,z=-411464.000},\n        name='Alioth Moon 4',\n        planetarySystemId=0,\n        radius=30330\n      },\n      [26]={\n        GM=68234043600,\n        bodyId=26,\n        center={x=-1404835.000,y=562655.000,z=-285074.000},\n        name='Sanctuary',\n        planetarySystemId=0,\n        radius=83400\n      },\n      [30]={\n        GM=211564034,\n        bodyId=30,\n        center={x=29214402.000,y=10907080.695,z=433858.200},\n        name='Thades Moon 1',\n        planetarySystemId=0,\n        radius=14002\n      },\n      [31]={\n        GM=264870000,\n        bodyId=31,\n        center={x=29404193.000,y=10432768.000,z=19554.131},\n        name='Thades Moon 2',\n        planetarySystemId=0,\n        radius=15000\n      },\n      [40]={\n        GM=141264000,\n        bodyId=40,\n        center={x=-13503090.000,y=55594325.000,z=769838.640},\n        name='Talemai Moon 2',\n        planetarySystemId=0,\n        radius=12000\n      },\n      [41]={\n        GM=106830900,\n        bodyId=41,\n        center={x=-12800515.000,y=55700259.000,z=325207.840},\n        name='Talemai Moon 3',\n        planetarySystemId=0,\n        radius=11000\n      },\n      [42]={\n        GM=264870000,\n        bodyId=42,\n        center={x=-13058408.000,y=55781856.000,z=740177.760},\n        name='Talemai Moon 1',\n        planetarySystemId=0,\n        radius=15000\n      },\n      [50]={\n        GM=499917600,\n        bodyId=50,\n        center={x=-43902841.780,y=22261034.700,z=-48862386.000},\n        name='Feli Moon 1',\n        planetarySystemId=0,\n        radius=14000\n      },\n      [70]={\n        GM=396912600,\n        bodyId=70,\n        center={x=58969616.000,y=29797945.000,z=57969449.000},\n        name='Sinnen Moon 1',\n        planetarySystemId=0,\n        radius=17000\n      },\n      [100]={\n        GM=13975172474,\n        bodyId=100,\n        center={x=98865536.000,y=-13534464.000,z=-934461.990},\n        name='Lacobus',\n        planetarySystemId=0,\n        radius=55650\n      },\n      [101]={\n        GM=264870000,\n        bodyId=101,\n        center={x=98905288.170,y=-13950921.100,z=-647589.530},\n        name='Lacobus Moon 3',\n        planetarySystemId=0,\n        radius=15000\n      },\n      [102]={\n        GM=444981600,\n        bodyId=102,\n        center={x=99180968.000,y=-13783862.000,z=-926156.400},\n        name='Lacobus Moon 1',\n        planetarySystemId=0,\n        radius=18000\n      },\n      [103]={\n        GM=211503600,\n        bodyId=103,\n        center={x=99250052.000,y=-13629215.000,z=-1059341.400},\n        name='Lacobus Moon 2',\n        planetarySystemId=0,\n        radius=14000\n      },\n      [110]={\n        GM=9204742375,\n        bodyId=110,\n        center={x=14165536.000,y=-85634465.000,z=-934464.300},\n        name='Symeon',\n        planetarySystemId=0,\n        radius=49050\n      },\n      [120]={\n        GM=7135606629,\n        bodyId=120,\n        center={x=2865536.700,y=-99034464.000,z=-934462.020},\n        name='Ion',\n        planetarySystemId=0,\n        radius=44950\n      },\n      [121]={\n        GM=106830900,\n        bodyId=121,\n        center={x=2472916.800,y=-99133747.000,z=-1133582.800},\n        name='Ion Moon 1',\n        planetarySystemId=0,\n        radius=11000\n      },\n      [122]={\n        GM=176580000,\n        bodyId=122,\n        center={x=2995424.500,y=-99275010.000,z=-1378480.700},\n        name='Ion Moon 2',\n        planetarySystemId=0,\n        radius=15000\n      }\n     }\n    }\n    end\nfunction PlanetRef()\n--[[\n  Provide coordinate transforms and access to kinematic related parameters\n  Author: JayleBreak\n  Usage (unit.start):\n  PlanetaryReference = require('planetref')\n  galaxyReference = PlanetaryReference(referenceTableSource)\n  helios = galaxyReference[0] -- PlanetaryReference.PlanetarySystem instance\n  alioth = helios[2]          -- PlanetaryReference.BodyParameters instance\n  Methods:\n    PlanetaryReference:getPlanetarySystem - based on planetary system ID.\n    PlanetaryReference.isMapPosition - 'true' if an instance of 'MapPosition'\n    PlanetaryReference.createBodyParameters - for entry into reference table\n    PlanetaryReference.BodyParameters - a class containing a body's information.\n    PlanetaryReference.MapPosition - a class for map coordinates\n    PlanetaryReference.PlanetarySystem - a container for planetary system info.\n    PlanetarySystem:castIntersections - from a position in a given direction.\n    PlanetarySystem:closestBody - to the specified coordinates.\n    PlanetarySystem:convertToBodyIdAndWorldCoordinates - from map coordinates.\n    PlanetarySystem:getBodyParameters - from reference table.\n    PlanetarySystem:getPlanetarySystemId - for the instance.\n    BodyParameters:convertToWorldCoordinates - from map coordinates\n    BodyParameters:convertToMapPosition - from world coordinates\n    BodyParameters:getAltitude - of world coordinates\n    BodyParameters:getDistance - from center to world coordinates\n    BodyParameters:getGravity - at a given position in world coordinates.\n  Description\n  An instance of the 'PlanetaryReference' \"class\" can contain transform and\n  kinematic reference information for all planetary systems in DualUniverse.\n  Each planetary system is identified by a numeric identifier. Currently,\n  the only planetary system, Helios, has the identifier: zero. This \"class\"\n  supports the indexing ('[]') operation which is equivalent to the\n  use of the 'getPlanetarySystem' method. It also supports the 'pairs()'\n  method for iterating over planetary systems.\n\n  An instance of the 'PlanetarySystem' \"class\" contains all reference\n  information for a specific system. It supports the indexing ('[]') and\n  'pairs()' functions which allows iteration over each \"body\" in the\n  system where the key is the numeric body ID. It also supports the\n  'tostring()' method.\n  An instance of the 'BodyParameters' \"class\" contains all reference\n  information for a single celestial \"body\" (a moon or planet). It supports\n  the 'tostring()' method, and contains the data members:\n          planetarySystemId - numeric planetary system ID\n          bodyId            - numeric body ID\n          radius            - radius of the body in meters (zero altitude)\n          center            - world coordinates of the body's center position\n          GM                - the gravitation parameter (g = GM/radius^2)\n  Note that the user is allowed to add custom fields (e.g. body name), but\n  should insure that complex table values have the '__tostring' metamethod\n  implemented.\n  Transform and Kinematics:\n  \"World\" coordinates is a cartesian coordinate system with an origin at an\n  arbitrary fixed point in a planetary system and with distances measured in\n  meters. The coordinates are expressible either as a simple table of 3 values\n  or an instance of the 'vec3' class.  In either case, the planetary system\n  identity is implicit.\n  \"Map\" coordinates is a geographic coordinate system with an origin at the\n  center of an identified (by a numeric value) celestial body which is a\n  member of an identified (also a numeric value) planetary system. Note that\n  the convention that latitude, longitude, and altitude values will be the\n  position's x, y, and z world coordinates in the special case of body ID 0.\n  The kinematic parameters in the reference data permit calculations of the\n  gravitational attraction of the celestial body on other objects.\n  Reference Data:\n  This is an example of reference data with a single entry assigned to\n  planetary system ID 0, and body ID 2 ('Alioth'):\n    referenceTable = {\n          [0] = { [2] = { planetarySystemId = 0,\n                          bodyId = 2,\n                          radius = 126068,\n                          center = vec3({x=-8, y=-8, z=-126303}),\n                          GM = 1.572199+11 } -- as in F=-GMm/r^2\n          }\n      }\n    ref=PlanetaryReference(referenceTable)\n  Collecting Reference Data:\n  A combination of information from the \"Map\" screen in the DU user interface,\n  and values reported by the DU Lua API can be the source of the reference\n  table's data (planetarySystemId, bodyId, and surfaceArea is from the user\n  interface):\n    referenceTable = {}\n    referenceTable[planetarySystemId][bodyId] =\n         PlanetaryReference.createBodyParameters(planetarySystemId,\n                                                 bodyId,\n                                                 surfaceArea,\n                                                 core.getConstructWorldPos(),\n                                                 core.getWorldVertical(),\n                                                 core.getAltitude(),\n                                                 core.g())\n  Adapting Data Sources:\n  Other sources of data can be adapted or converted. An example of adapting a\n  table, defined in the file: 'planets.lua', containing information on a single\n  planetary system and using celestial body name as the key follows (note that\n  a 'name' field is added to the BodyParameters instance transparently after\n  construction, and the '__pairs' meta function is required to support the\n  'closestBody' and '__tostring' methods):\n    ref=PlanetaryReference(\n        {[0] = setmetatable(require('planets'),\n                        { __index = function(bodies, bodyId)\n                             for _,v in pairs(bodies) do\n                                 if v and v.bodyId == bodyId then return v end\n                             end\n                             return nil\n                           end,\n                         __pairs = function(bodies)\n                             return function(t, k)\n                                     local nk, nv = next(t, k)\n                                     if nv then\n                                         local GM = nv.gravity * nv.radius^2\n                                         local bp = BodyParameters(0,\n                                                                   nv.id,\n                                                                   nv.radius,\n                                                                   nv.pos,\n                                                                   GM)\n                                         bp.name = nk\n                                         return nk, bp\n                                    end\n                                    return nk, nv\n                                 end, bodies, nil\n                           end })\n    })\n\n  Converting Data Sources:\n  An instance of 'PlanetaryReference' that has been adapted to a data source\n  can be used to convert that source to simple table. For example,\n  using the adapted instance shown above:\n    load('convertedData=' .. tostring(ref))()\n    newRef=PlanetaryReference(convertedData)\n  Also See: kepler.lua\n  ]]--\n--[[                    START OF LOCAL IMPLEMENTATION DETAILS             ]]--\n-- Type checks\nlocal function isNumber(n)  return type(n)           == 'number' end\nlocal function isSNumber(n) return type(tonumber(n)) == 'number' end\nlocal function isTable(t)   return type(t)           == 'table'  end\nlocal function isString(s)  return type(s)           == 'string' end\nlocal function isVector(v)  return isTable(v)\n                                    and isNumber(v.x and v.y and v.z) end\nlocal function isMapPosition(m) return isTable(m) and isNumber(m.latitude  and\n                                                               m.longitude and\n                                                               m.altitude  and\n                                                               m.bodyId    and\n                                                               m.systemId) end\n-- Constants\nlocal deg2rad    = math.pi/180\nlocal rad2deg    = 180/math.pi\nlocal epsilon    = 1e-10\nlocal num        = ' *([+-]?%d+%.?%d*e?[+-]?%d*)'\nlocal posPattern = '::pos{' .. num .. ',' .. num .. ',' ..  num .. ',' ..\n                   num ..  ',' .. num .. '}'\n-- Utilities\nlocal utils  = require('cpml.utils')\nlocal vec3   = require('cpml.vec3')\nlocal clamp  = utils.clamp\nlocal function float_eq(a,b)\n    if a == 0 then return math.abs(b) < 1e-09 end\n    if b == 0 then return math.abs(a) < 1e-09 end\n    return math.abs(a - b) < math.max(math.abs(a),math.abs(b))*epsilon\nend\nlocal function formatNumber(n)\n    local result = string.gsub(\n                    string.reverse(string.format('%.4f',n)),\n                    '^0*%.?','')\n    return result == '' and '0' or string.reverse(result)\nend\nlocal function formatValue(obj)\n    if isVector(obj) then\n        return string.format('{x=%.3f,y=%.3f,z=%.3f}', obj.x, obj.y, obj.z)\n    end\n    if isTable(obj) and not getmetatable(obj) then\n        local list = {}\n        local nxt  = next(obj)\n        if type(nxt) == 'nil' or nxt == 1 then -- assume this is an array\n            list = obj\n        else\n            for k,v in pairs(obj) do\n                local value = formatValue(v)\n                if type(k) == 'number' then\n                    table.insert(list, string.format('[%s]=%s', k, value))\n                else\n                    table.insert(list, string.format('%s=%s',   k, value))\n                end\n            end\n        end\n        return string.format('{%s}', table.concat(list, ','))\n    end\n    if isString(obj) then\n        return string.format(\"'%s'\", obj:gsub(\"'\",[[\\']]))\n    end\n    return tostring(obj)\nend\n-- CLASSES\n-- BodyParameters: Attributes of planetary bodies (planets and moons)\nlocal BodyParameters = {}\nBodyParameters.__index = BodyParameters\nBodyParameters.__tostring =\n    function(obj, indent)\n        local sep = indent or ''\n        local keys = {}\n        for k in pairs(obj) do table.insert(keys, k) end\n        table.sort(keys)\n        local list = {}\n        for _, k in ipairs(keys) do\n            local value = formatValue(obj[k])\n            if type(k) == 'number' then\n                table.insert(list, string.format('[%s]=%s', k, value))\n            else\n                table.insert(list, string.format('%s=%s', k, value))\n            end\n        end\n        if indent then\n            return string.format('%s%s',\n                                 indent,\n                                 table.concat(list, ',\\n' .. indent))\n        end\n        return string.format('{%s}', table.concat(list, ','))\n    end\nBodyParameters.__eq = function(lhs, rhs)\n        return lhs.planetarySystemId == rhs.planetarySystemId and\n               lhs.bodyId            == rhs.bodyId            and\n               float_eq(lhs.radius, rhs.radius)               and\n               float_eq(lhs.center.x, rhs.center.x)           and\n               float_eq(lhs.center.y, rhs.center.y)           and\n               float_eq(lhs.center.z, rhs.center.z)           and\n               float_eq(lhs.GM, rhs.GM)\n    end\nlocal function mkBodyParameters(systemId, bodyId, radius, worldCoordinates, GM)\n    -- 'worldCoordinates' can be either table or vec3\n    assert(isSNumber(systemId),\n           'Argument 1 (planetarySystemId) must be a number:' .. type(systemId))\n    assert(isSNumber(bodyId),\n           'Argument 2 (bodyId) must be a number:' .. type(bodyId))\n    assert(isSNumber(radius),\n           'Argument 3 (radius) must be a number:' .. type(radius))\n    assert(isTable(worldCoordinates),\n           'Argument 4 (worldCoordinates) must be a array or vec3.' ..\n           type(worldCoordinates))\n    assert(isSNumber(GM),\n           'Argument 5 (GM) must be a number:' .. type(GM))\n    return setmetatable({planetarySystemId = tonumber(systemId),\n                         bodyId            = tonumber(bodyId),\n                         radius            = tonumber(radius),\n                         center            = vec3(worldCoordinates),\n                         GM                = tonumber(GM) }, BodyParameters)\nend\n-- MapPosition: Geographical coordinates of a point on a planetary body.\nlocal MapPosition = {}\nMapPosition.__index = MapPosition\nMapPosition.__tostring = function(p)\n        return string.format('::pos{%d,%d,%s,%s,%s}',\n                             p.systemId,\n                             p.bodyId,\n                             formatNumber(p.latitude*rad2deg),\n                             formatNumber(p.longitude*rad2deg),\n                             formatNumber(p.altitude))\n    end\nMapPosition.__eq       = function(lhs, rhs)\n        return lhs.bodyId   == rhs.bodyId              and\n               lhs.systemId == rhs.systemId            and\n               float_eq(lhs.latitude,   rhs.latitude)  and\n               float_eq(lhs.altitude,   rhs.altitude)  and\n               (float_eq(lhs.longitude, rhs.longitude) or\n                float_eq(lhs.latitude, math.pi/2)      or\n                float_eq(lhs.latitude, -math.pi/2))\n    end\n-- latitude and longitude are in degrees while altitude is in meters\nlocal function mkMapPosition(overload, bodyId, latitude, longitude, altitude)\n    local systemId = overload -- Id or '::pos{...}' string\n    if isString(overload) and not longitude and not altitude and\n                              not bodyId    and not latitude then\n        systemId, bodyId, latitude, longitude, altitude =\n                                            string.match(overload, posPattern)\n        assert(systemId, 'Argument 1 (position string) is malformed.')\n    else\n        assert(isSNumber(systemId),\n               'Argument 1 (systemId) must be a number:' .. type(systemId))\n        assert(isSNumber(bodyId),\n               'Argument 2 (bodyId) must be a number:' .. type(bodyId))\n        assert(isSNumber(latitude),\n               'Argument 3 (latitude) must be in degrees:' .. type(latitude))\n        assert(isSNumber(longitude),\n               'Argument 4 (longitude) must be in degrees:' .. type(longitude))\n        assert(isSNumber(altitude),\n               'Argument 5 (altitude) must be in meters:' .. type(altitude))\n    end\n    systemId  = tonumber(systemId)\n    bodyId    = tonumber(bodyId)\n    latitude  = tonumber(latitude)\n    longitude = tonumber(longitude)\n    altitude  = tonumber(altitude)\n    if bodyId == 0 then -- this is a hack to represent points in space\n        return setmetatable({latitude  = latitude,\n                             longitude = longitude,\n                             altitude  = altitude,\n                             bodyId    = bodyId,\n                             systemId  = systemId}, MapPosition)\n    end\n    return setmetatable({latitude  = deg2rad*clamp(latitude, -90, 90),\n                         longitude = deg2rad*(longitude % 360),\n                         altitude  = altitude,\n                         bodyId    = bodyId,\n                         systemId  = systemId}, MapPosition)\nend\n-- PlanetarySystem - map body IDs to BodyParameters\nlocal PlanetarySystem = {}\nPlanetarySystem.__index = PlanetarySystem\nPlanetarySystem.__tostring =\n    function (obj, indent)\n        local sep = indent and (indent .. '  ' )\n        local bdylist = {}\n        local keys = {}\n        for k in pairs(obj) do table.insert(keys, k) end\n        table.sort(keys)\n        for _, bi in ipairs(keys) do\n            bdy = obj[bi]\n            local bdys = BodyParameters.__tostring(bdy, sep)\n            if indent then\n                table.insert(bdylist,\n                             string.format('[%s]={\\n%s\\n%s}',\n                                           bi, bdys, indent))\n            else\n                table.insert(bdylist, string.format('  [%s]=%s', bi, bdys))\n            end\n        end\n        if indent then\n            return string.format('\\n%s%s%s',\n                                 indent,\n                                 table.concat(bdylist, ',\\n' .. indent),\n                                 indent)\n        end\n        return string.format('{\\n%s\\n}', table.concat(bdylist, ',\\n'))\n    end\nlocal function mkPlanetarySystem(referenceTable)\n    local atlas = {}\n    local pid\n    for _, v in pairs(referenceTable) do\n        local id = v.planetarySystemId\n        if type(id) ~= 'number' then\n            error('Invalid planetary system ID: ' .. tostring(id))\n        elseif pid and id ~= pid then\n            error('Mismatch planetary system IDs: ' .. id .. ' and '\n                  .. pid)\n        end\n        local bid = v.bodyId\n        if type(bid) ~= 'number' then\n            error('Invalid body ID: ' .. tostring(bid))\n        elseif atlas[bid] then\n            error('Duplicate body ID: ' .. tostring(bid))\n        end\n        setmetatable(v.center, getmetatable(vec3.unit_x))\n        atlas[bid] = setmetatable(v, BodyParameters)\n        pid = id\n    end\n    return setmetatable(atlas, PlanetarySystem)\nend\n-- PlanetaryReference - map planetary system ID to PlanetarySystem\nPlanetaryReference = {}\nlocal function mkPlanetaryReference(referenceTable)\n    return setmetatable({ galaxyAtlas = referenceTable or {} },\n                          PlanetaryReference)\nend\nPlanetaryReference.__index        =\n    function(t,i)\n        if type(i) == 'number' then\n            local system = t.galaxyAtlas[i]\n            return mkPlanetarySystem(system)\n        end\n        return rawget(PlanetaryReference, i)\n    end\nPlanetaryReference.__pairs        =\n    function(obj)\n        return  function(t, k)\n                    local nk, nv = next(t, k)\n                    return nk, nv and mkPlanetarySystem(nv)\n                end, obj.galaxyAtlas, nil\n    end\nPlanetaryReference.__tostring     =\n    function (obj)\n        local pslist = {}\n        for _,ps in pairs(obj or {}) do\n            local psi = ps:getPlanetarySystemId()\n            local pss = PlanetarySystem.__tostring(ps, '    ')\n            table.insert(pslist,\n                         string.format('  [%s]={%s\\n  }', psi, pss))\n        end\n        return string.format('{\\n%s\\n}\\n', table.concat(pslist,',\\n'))\n    end\n--[[                       START OF PUBLIC INTERFACE                       ]]--\n-- PlanetaryReference CLASS METHODS:\n--\n-- BodyParameters - create an instance of BodyParameters class\n-- planetarySystemId  [in]: the body's planetary system ID.\n-- bodyId             [in]: the body's ID.\n-- radius             [in]: the radius in meters of the planetary body.\n-- bodyCenter         [in]: the world coordinates of the center (vec3 or table).\n-- GM                 [in]: the body's standard gravitational parameter.\n-- return: an instance of BodyParameters class.\n--\nPlanetaryReference.BodyParameters = mkBodyParameters\n--\n-- MapPosition - create an instance of the MapPosition class\n-- overload [in]: either a planetary system ID or a position string ('::pos...')\n-- bodyId [in]:   (ignored if overload is a position string) the body's ID.\n-- latitude [in]: (ignored if overload is a position string) the latitude.\n-- longitude [in]:(ignored if overload is a position string) the longitude.\n-- altitude [in]: (ignored if overload is a position string) the altitude.\n-- return: the class instance\n--\nPlanetaryReference.MapPosition    = mkMapPosition\n--\n-- PlanetarySystem - create an instance of PlanetarySystem class\n-- referenceData [in]: a table (indexed by bodyId) of body reference info.\n-- return: the class instance\n--\nPlanetaryReference.PlanetarySystem = mkPlanetarySystem\n--\n-- createBodyParameters - create an instance of BodyParameters class\n-- planetarySystemId  [in]: the body's planetary system ID.\n-- bodyId             [in]: the body's ID.\n-- surfaceArea        [in]: the body's surface area in square meters.\n-- aPosition          [in]: world coordinates of a position near the body.\n-- verticalAtPosition [in]: a vector pointing towards the body center.\n-- altitudeAtPosition [in]: the altitude in meters at the position.\n-- gravityAtPosition  [in]: the magnitude of the gravitational acceleration.\n-- return: an instance of BodyParameters class.\n--\nfunction PlanetaryReference.createBodyParameters(planetarySystemId,\n                                                 bodyId,\n                                                 surfaceArea,\n                                                 aPosition,\n                                                 verticalAtPosition,\n                                                 altitudeAtPosition,\n                                                 gravityAtPosition)\n    assert(isSNumber(planetarySystemId),\n           'Argument 1 (planetarySystemId) must be a number:' ..\n           type(planetarySystemId))\n    assert(isSNumber(bodyId),\n           'Argument 2 (bodyId) must be a number:' .. type(bodyId))\n    assert(isSNumber(surfaceArea),\n           'Argument 3 (surfaceArea) must be a number:' .. type(surfaceArea))\n    assert(isTable(aPosition),\n           'Argument 4 (aPosition) must be an array or vec3:' ..\n           type(aPosition))\n    assert(isTable(verticalAtPosition),\n           'Argument 5 (verticalAtPosition) must be an array or vec3:' ..\n           type(verticalAtPosition))\n    assert(isSNumber(altitudeAtPosition),\n           'Argument 6 (altitude) must be in meters:' ..\n           type(altitudeAtPosition))\n    assert(isSNumber(gravityAtPosition),\n           'Argument 7 (gravityAtPosition) must be number:' ..\n           type(gravityAtPosition))\n    local radius   = math.sqrt(surfaceArea/4/math.pi)\n    local distance = radius + altitudeAtPosition\n    local center   = vec3(aPosition) + distance*vec3(verticalAtPosition)\n    local GM       = gravityAtPosition * distance * distance\n    return mkBodyParameters(planetarySystemId, bodyId, radius, center, GM)\nend\n--\n-- isMapPosition - check for the presence of the 'MapPosition' fields\n-- valueToTest [in]: the value to be checked\n-- return: 'true' if all required fields are present in the input value\n--\nPlanetaryReference.isMapPosition  = isMapPosition\n-- PlanetaryReference INSTANCE METHODS:\n--\n-- getPlanetarySystem - get the planetary system using ID or MapPosition as key\n-- overload [in]: either the planetary system ID or a MapPosition that has it.\n-- return: instance of 'PlanetarySystem' class or nil on error\n--\nfunction PlanetaryReference:getPlanetarySystem(overload)\n    --if galaxyAtlas then\n        local planetarySystemId = overload\n        if isMapPosition(overload) then\n            planetarySystemId = overload.systemId\n        end\n        if type(planetarySystemId) == 'number' then\n            local system = self.galaxyAtlas[i]\n            if system then\n                if getmetatable(nv) ~= PlanetarySystem then\n                    system = mkPlanetarySystem(system)\n                end\n                return system\n            end\n        end\n    --end\n    --return nil\nend\n-- PlanetarySystem INSTANCE METHODS:\n--\n-- castIntersections - Find the closest body that intersects a \"ray cast\".\n-- origin [in]: the origin of the \"ray cast\" in world coordinates\n-- direction [in]: the direction of the \"ray cast\" as a 'vec3' instance.\n-- sizeCalculator [in]: (default: returns 1.05*radius) Returns size given body.\n-- bodyIds[in]: (default: all IDs in system) check only the given IDs.\n-- return: The closest body that blocks the cast or 'nil' if none.\n--\nfunction PlanetarySystem:castIntersections(origin,\n                                           direction,\n                                           sizeCalculator,\n                                           bodyIds)\n    local sizeCalculator = sizeCalculator or\n                            function (body) return 1.05*body.radius end\n    local candidates = {}\n    if bodyIds then\n        for _,i in ipairs(bodyIds) do candidates[i] = self[i] end\n    else\n        bodyIds = {}\n        for k,body in pairs(self) do\n            table.insert(bodyIds, k)\n            candidates[k] = body\n        end\n    end\n    local function compare(b1,b2)\n        local v1 = candidates[b1].center - origin\n        local v2 = candidates[b2].center - origin\n        return v1:len() < v2:len()\n    end\n    table.sort(bodyIds, compare)\n    local dir = direction:normalize()\n    for i, id in ipairs(bodyIds) do\n        local body   = candidates[id]\n        local c_oV3  = body.center - origin\n        local radius = sizeCalculator(body)\n        local dot    = c_oV3:dot(dir)\n        local desc   = dot^2 - (c_oV3:len2() - radius^2)\n        if desc >= 0 then\n            local root     = math.sqrt(desc)\n            local farSide  = dot + root\n            local nearSide = dot - root\n            if nearSide > 0 then\n                return body, farSide, nearSide\n            elseif farSide > 0 then\n                return body, farSide, nil\n            end\n        end\n    end\n    return nil, nil, nil\nend\n--\n-- closestBody - find the closest body to a given set of world coordinates\n-- coordinates       [in]: the world coordinates of position in space\n-- return: an instance of the BodyParameters object closest to 'coordinates'\n--\nfunction PlanetarySystem:closestBody(coordinates)\n    assert(type(coordinates) == 'table', 'Invalid coordinates.')\n    local minDistance2, body\n    local coord = vec3(coordinates)\n    for _,params in pairs(self) do\n        local distance2 = (params.center - coord):len2()\n        if not body or distance2 < minDistance2 then\n            body         = params\n            minDistance2 = distance2\n        end\n    end\n    return body\nend\n--\n-- convertToBodyIdAndWorldCoordinates - map to body Id and world coordinates\n-- overload [in]: an instance of MapPosition or a position string ('::pos...)\n-- return: a vec3 instance containing the world coordinates or 'nil' on error.\n--\nfunction PlanetarySystem:convertToBodyIdAndWorldCoordinates(overload)\n    local mapPosition = overload\n    if isString(overload) then\n        mapPosition = mkMapPosition(overload)\n    end\n    if mapPosition.bodyId == 0 then\n        return 0, vec3(mapPosition.latitude,\n                       mapPosition.longitude,\n                       mapPosition.altitude)\n    end\n    local params = self:getBodyParameters(mapPosition)\n    if params then\n        return mapPosition.bodyId,\n               params:convertToWorldCoordinates(mapPosition)\n    end\nend\n--\n-- getBodyParameters - get or create an instance of BodyParameters class\n-- overload [in]: either an instance of MapPosition or a body's ID.\n-- return: a BodyParameters instance or 'nil' if body ID is not found.\n--\nfunction PlanetarySystem:getBodyParameters(overload)\n    local bodyId = overload\n    if isMapPosition(overload) then\n        bodyId = overload.bodyId\n    end\n    assert(isSNumber(bodyId),\n               'Argument 1 (bodyId) must be a number:' .. type(bodyId))\n    return self[bodyId]\nend\n--\n-- getPlanetarySystemId - get the planetary system ID for this instance\n-- return: the planetary system ID or nil if no planets are in the system.\n--\nfunction PlanetarySystem:getPlanetarySystemId()\n    local k, v = next(self)\n    return v and v.planetarySystemId\nend\n-- BodyParameters INSTANCE METHODS:\n--\n-- convertToMapPosition - create an instance of MapPosition from coordinates\n-- worldCoordinates [in]: the world coordinates of the map position.\n-- return: an instance of MapPosition class\n--\nfunction BodyParameters:convertToMapPosition(worldCoordinates)\n    assert(isTable(worldCoordinates),\n           'Argument 1 (worldCoordinates) must be an array or vec3:' ..\n           type(worldCoordinates))\n    local worldVec  = vec3(worldCoordinates)\n    if self.bodyId == 0 then\n        return setmetatable({latitude  = worldVec.x,\n                             longitude = worldVec.y,\n                             altitude  = worldVec.z,\n                             bodyId    = 0,\n                             systemId  = self.planetarySystemId}, MapPosition)\n    end\n    local coords    = worldVec - self.center\n    local distance  = coords:len()\n    local altitude  = distance - self.radius\n    local latitude  = 0\n    local longitude = 0\n    if not float_eq(distance, 0) then\n        local phi = math.atan(coords.y, coords.x)\n        longitude = phi >= 0 and phi or (2*math.pi + phi)\n        latitude  = math.pi/2 - math.acos(coords.z/distance)\n    end\n    return setmetatable({latitude  = latitude,\n                         longitude = longitude,\n                         altitude  = altitude,\n                         bodyId    = self.bodyId,\n                         systemId  = self.planetarySystemId}, MapPosition)\nend\n--\n-- convertToWorldCoordinates - convert a map position to world coordinates\n-- overload [in]: an instance of MapPosition or a position string ('::pos...')\n--\nfunction BodyParameters:convertToWorldCoordinates(overload)\n    local mapPosition = isString(overload) and\n                                           mkMapPosition(overload) or overload\n    if mapPosition.bodyId == 0 then -- support deep space map position\n        return vec3(mapPosition.latitude,\n                    mapPosition.longitude,\n                    mapPosition.altitude)\n    end\n    assert(isMapPosition(mapPosition),\n           'Argument 1 (mapPosition) is not an instance of \"MapPosition\".')\n    assert(mapPosition.systemId == self.planetarySystemId,\n           'Argument 1 (mapPosition) has a different planetary system ID.')\n    assert(mapPosition.bodyId == self.bodyId,\n           'Argument 1 (mapPosition) has a different planetary body ID.')\n    local xproj = math.cos(mapPosition.latitude)\n    return self.center + (self.radius + mapPosition.altitude) *\n           vec3(xproj*math.cos(mapPosition.longitude),\n                xproj*math.sin(mapPosition.longitude),\n                math.sin(mapPosition.latitude))\nend\n--\n-- getAltitude - calculate the altitude of a point given in world coordinates.\n-- worldCoordinates [in]: the world coordinates of the point.\n-- return: the altitude in meters\n--\nfunction BodyParameters:getAltitude(worldCoordinates)\n    return (vec3(worldCoordinates) - self.center):len() - self.radius\nend\n--\n-- getDistance - calculate the distance to a point given in world coordinates.\n-- worldCoordinates [in]: the world coordinates of the point.\n-- return: the distance in meters\n--\nfunction BodyParameters:getDistance(worldCoordinates)\n    return (vec3(worldCoordinates) - self.center):len()\nend\n--\n-- getGravity - calculate the gravity vector induced by the body.\n-- worldCoordinates [in]: the world coordinates of the point.\n-- return: the gravity vector in meter/seconds^2\n--\nfunction BodyParameters:getGravity(worldCoordinates)\n    local radial = self.center - vec3(worldCoordinates) -- directed towards body\n    local len2   = radial:len2()\n    return (self.GM/len2) * radial/math.sqrt(len2)\nend\n-- end of module\nreturn setmetatable(PlanetaryReference,\n                    { __call = function(_,...)\n                                    return mkPlanetaryReference(...)\n                               end })\nend\nfunction Keplers()\n    --[[\n  Provides methods for computing orbital information for an object\n  Usage:\n  Kepler = require('autoconf.custom.kepler')\n  alioth = Kepler({ GM=157470826617,\n                    bodyId=2,\n                    center={x=-8.000,y=-8.000,z=-126303.000},\n                    name='Alioth',\n                    planetarySystemId=0,\n                    radius=126068\n                  })\n  altitude = 6000\n  position = '::pos{0,2,0,0,6000}'\n  e, o     = alioth:escapeAndOrbitalSpeed(altitude)\n  orbit    = alioth:orbitalParameters(position, {0, o+1, 0})\n  print(\"Eccentricity \" .. orbit.eccentricity)\n  print(\"Perihelion \" .. orbit.periapsis.altitude)\n  print(\"Max. speed \" .. orbit.periapsis.speed)\n  print(\"Circular orbit speed \" .. orbit.periapsis.circularOrbitSpeed)\n  print(\"Aphelion \"  .. orbit.apoapsis.altitude)\n  print(\"Min. speed \" .. orbit.apoapsis.speed)\n  print(\"Orbital period \" .. orbit.period)\n  --- output:\n    Eccentricity 0.0018324307017878\n    Perihelion 6000.0\n    Max. speed 1092.9462297033\n    Circular orbit speed 1091.9462297033\n    Aphelion 6484.8994605062\n    Min. speed 1088.9480596194\n    Orbital period 762.02818214049\n  Methods:\n    Kepler:escapeAndOrbitalSpeed - for a given celestial body and altitude.\n    Kepler:orbitalParameters - for a given massless object and a celestial body.\n  Description\n  The motion of an object in the vicinity of substantially larger mass is\n  in the domain of the \"2-body problem\". By assuming the object whose motion\n  is of interest is of negligable mass simplifies the calculations of:\n  the speed to escape the body, the speed of a circular orbit, and the\n  parameters defining the orbit of the object (or the lack of orbit as the\n  case may be).\n  Orbital Parameters:\n     periapsis - the closest approach to the planet\n      apoapsis - the furthest point from the planet if in orbit (otherwise nil)\n  eccentricity - 0 for circular orbits\n                <1 for elliptical orbits\n                 1 for parabiolic trajectory\n                >1 for hyperbolic trajectory\n        period - time (in seconds) to complete an orbit\n  Also See: planetref.lua\n]]--\nlocal vec3       = require('cpml.vec3')\nlocal PlanetRef  = PlanetRef()\nlocal function isString(s) return type(s)   == 'string' end\nlocal function isTable(t)  return type(t)   == 'table'  end\nlocal function float_eq(a,b)\n    if a == 0 then return math.abs(b) < 1e-09 end\n    if b == 0 then return math.abs(a) < 1e-09 end\n    return math.abs(a - b) < math.max(math.abs(a),math.abs(b))*epsilon\nend\nKepler = {}\nKepler.__index = Kepler\n--\n-- escapeAndOrbitalSpeed - speed required to escape and for a circular orbit\n-- altitude [in]: the height of the orbit in meters above \"sea-level\"\n-- return: the speed in m/s needed to escape the celestial body and to orbit it.\n--\nfunction Kepler:escapeAndOrbitalSpeed(altitude)\n    assert(self.body)\n    -- P = -GMm/r and KE = mv^2/2 (no lorentz factor used)\n    -- mv^2/2 = GMm/r\n    -- v^2 = 2GM/r\n    -- v = sqrt(2GM/r1)\n    local distance = altitude + self.body.radius\n    if not float_eq(distance, 0) then\n        local orbit = math.sqrt(self.body.GM/distance)\n        return math.sqrt(2)*orbit, orbit\n    end\n    return nil, nil\nend\n--\n-- orbitalParameters: determine the orbital elements for a two-body system.\n-- overload [in]: the world coordinates or map coordinates of a massless object.\n-- velocity [in]: The velocity of the massless point object in m/s.\n-- return: the 6 orbital elements for the massless object.\n--\nfunction Kepler:orbitalParameters(overload, velocity)\n    assert(self.body)\n    assert(isTable(overload) or isString(overload))\n    assert(isTable(velocity))\n    local pos = (isString(overload) or PlanetRef.isMapPosition(overload)) and\n                            self.body:convertToWorldCoordinates(overload) or\n                vec3(overload)\n    local v   = vec3(velocity)\n    local r   = pos - self.body.center\n    local v2  = v:len2()\n    local d   = r:len()\n    local mu  = self.body.GM\n    local e   = ((v2 - mu/d)*r - r:dot(v)*v)/mu\n    local a   = mu/(2*mu/d - v2)\n    local ecc = e:len()\n    local dir = e:normalize()\n    local pd  = a*(1-ecc)\n    local ad  = a*(1+ecc)\n    local per = pd*dir + self.body.center\n    local apo = ecc <= 1 and -ad*dir + self.body.center or nil\n    local trm = math.sqrt(a*mu*(1-ecc*ecc))\n    local Period = apo and 2*math.pi*math.sqrt(a^3/mu)\n    -- These are great and all, but, I need more.\n    local trueAnomaly = math.acos((e:dot(r))/(ecc*d))\n    if r:dot(v) < 0 then\n        trueAnomaly = -(trueAnomaly - 2*math.pi)\n    end\n    -- Apparently... cos(EccentricAnomaly) = (cos(trueAnomaly) + eccentricity)/(1 + eccentricity * cos(trueAnomaly))\n    local EccentricAnomaly = math.acos((math.cos(trueAnomaly) + ecc)/(1 + ecc * math.cos(trueAnomaly)))\n    -- Then.... apparently if this is below 0, we should add 2pi to it\n    -- I think also if it's below 0, we're past the apoapsis?\n    local timeTau = EccentricAnomaly\n    if timeTau < 0 then\n        timeTau = timeTau + 2*math.pi\n    end\n    -- So... time since periapsis...\n    -- Is apparently easy if you get mean anomly.  t = M/n where n is mean motion, = 2*pi/Period\n\n\n    local MeanAnomaly = timeTau - ecc * math.sin(timeTau)\n    local TimeSincePeriapsis = MeanAnomaly/(2*math.pi/Period)\n    --system.print(MeanAnomaly .. \" - \" .. TimeSincePeriapsis .. \" - \" .. Period .. \" - \" .. EccentricAnomaly .. \" - \" .. timeTau .. \" - \" .. trueAnomaly)\n    -- Mean anom is 0 at periapsis, positive before it... and positive after it.\n    -- I guess this is why I needed to use timeTau and not EccentricAnomaly here\n\n    local TimeToPeriapsis = Period - TimeSincePeriapsis\n    local TimeToApoapsis = TimeToPeriapsis + Period/2\n    if trueAnomaly - math.pi > 0 then -- TBH I think something's wrong in my formulas because I needed this.\n        TimeToPeriapsis = TimeSincePeriapsis\n        TimeToApoapsis = TimeToPeriapsis + Period/2\n    end\n    if TimeToApoapsis > Period then\n        TimeToApoapsis = TimeToApoapsis - Period\n    end\n    return { periapsis       = { position           = per,\n                                 speed              = trm/pd,\n                                 circularOrbitSpeed = math.sqrt(mu/pd),\n                                 altitude           = pd - self.body.radius},\n             apoapsis        = apo and\n                               { position           = apo,\n                                 speed              = trm/ad,\n                                 circularOrbitSpeed = math.sqrt(mu/ad),\n                                 altitude           = ad - self.body.radius},\n             currentVelocity = v,\n             currentPosition = pos,\n             eccentricity    = ecc,\n             period          = Period,\n             eccentricAnomaly = EccentricAnomaly,\n             meanAnomaly = MeanAnomaly,\n             timeToPeriapsis = TimeToPeriapsis,\n             timeToApoapsis = TimeToApoapsis\n           }\nend\n\nlocal function new(bodyParameters)\n    local params = PlanetRef.BodyParameters(bodyParameters.planetarySystemId,\n                                            bodyParameters.bodyId,\n                                            bodyParameters.radius,\n                                            bodyParameters.center,\n                                            bodyParameters.GM)\n    return setmetatable({body = params}, Kepler)\nend\nreturn setmetatable(Kepler, { __call = function(_,...) return new(...) end })\nend\nfunction Kinematics()\n    --[[\n  DualUniverse kinematic equations\n  Author: JayleBreak\n  Usage (unit.start):\n  Kinematics = require('autoconf.custom.kinematics')\n  Methods:\n   computeAccelerationTime - \"relativistic\" version of t = (vf - vi)/a\n   computeDistanceAndTime - Return distance & time needed to reach final speed.\n   computeTravelTime - \"relativistic\" version of t=(sqrt(2ad+v^2)-v)/a\n  Description\n  DualUniverse increases the effective mass of constructs as their absolute\n  speed increases by using the \"lorentz\" factor (from relativity) as the scale\n  factor.  This results in an upper bound on the absolute speed of constructs\n  (excluding \"warp\" drive) that is set to 30 000 KPH (8 333 MPS). This module\n  provides utilities for computing some physical quantities taking this\n  scaling into account.\n]]--\nlocal Kinematic = {} -- just a namespace\nlocal C       = 30000000/3600\nlocal C2      = C*C\nlocal ITERATIONS = 100 -- iterations over engine \"warm-up\" period\nlocal function lorentz(v) return 1/math.sqrt(1 - v*v/C2) end\n--\n-- computeAccelerationTime - \"relativistic\" version of t = (vf - vi)/a\n-- initial      [in]: initial (positive) speed in meters per second.\n-- acceleration [in]: constant acceleration until 'finalSpeed' is reached.\n-- final        [in]: the speed at the end of the time interval.\n-- return: the time in seconds spent in traversing the distance\n--\nfunction Kinematic.computeAccelerationTime(initial, acceleration, final)\n    -- The low speed limit of following is: t=(vf-vi)/a (from: vf=vi+at)\n    local k1 = C*math.asin(initial/C)\n    return (C * math.asin(final/C) - k1)/acceleration\nend\n--\n-- computeDistanceAndTime - Return distance & time needed to reach final speed.\n-- initial[in]:     Initial speed in meters per second.\n-- final[in]:       Final speed in meters per second.\n-- restMass[in]:    Mass of the construct at rest in Kg.\n-- thrust[in]:      Engine's maximum thrust in Newtons.\n-- t50[in]:         (default: 0) Time interval to reach 50% thrust in seconds.\n-- brakeThrust[in]: (default: 0) Constant thrust term when braking.\n-- return: Distance (in meters), time (in seconds) required for change.\n--\nfunction Kinematic.computeDistanceAndTime(initial,\n                                          final,\n                                          restMass,\n                                          thrust,\n                                          t50,\n                                          brakeThrust)\n    -- This function assumes that the applied thrust is colinear with the\n    -- velocity. Furthermore, it does not take into account the influence\n    -- of gravity, not just in terms of its impact on velocity, but also\n    -- its impact on the orientation of thrust relative to velocity.\n    -- These factors will introduce (usually) small errors which grow as\n    -- the length of the trip increases.\n    t50            = t50 or 0\n    brakeThrust    = brakeThrust or 0 -- usually zero when accelerating\n    local tau0     = lorentz(initial)\n    local speedUp  = initial <= final\n    local a0       = thrust * (speedUp and 1 or -1)/restMass\n    local b0       = -brakeThrust/restMass\n    local totA     = a0+b0\n    if speedUp and totA <= 0 or not speedUp and totA >= 0 then\n        return -1, -1 -- no solution\n    end\n    local distanceToMax, timeToMax = 0, 0\n    -- If, the T50 time is set, then assume engine is at zero thrust and will\n    -- reach full thrust in 2*T50 seconds. Thrust curve is given by:\n    -- Thrust: F(z)=(a0*(1+sin(z))+2*b0)/2 where z=pi*(t/t50 - 1)/2\n    -- Acceleration is given by F(z)/m(z) where m(z) = m/sqrt(1-v^2/c^2)\n    -- or v(z)' = (a0*(1+sin(z))+2*b0)*sqrt(1-v(z)^2/c^2)/2\n    if a0 ~= 0 and t50 > 0 then\n        -- Closed form solution for velocity exists:\n        -- v(t) = -c*tan(w)/sqrt(tan(w)^2+1) => w = -asin(v/c)\n        -- w=(pi*t*(a0/2+b0)-a0*t50*sin(pi*t/2/t50)+*pi*c*k1)/pi/c\n        -- @ t=0, v(0) = vi\n        -- pi*c*k1/pi/c = -asin(vi/c)\n        -- k1 = asin(vi/c)\n        local k1  = math.asin(initial/C)\n        local c1  = math.pi*(a0/2+b0)\n        local c2  = a0*t50\n        local c3  = C*math.pi\n        local v = function(t)\n            local w  = (c1*t - c2*math.sin(math.pi*t/2/t50) + c3*k1)/c3\n            local tan = math.tan(w)\n            return C*tan/math.sqrt(tan*tan+1)\n        end\n        local speedchk = speedUp and function(s) return s >= final end or\n                                     function(s) return s <= final end\n        timeToMax  = 2*t50\n        if speedchk(v(timeToMax)) then\n            local lasttime = 0\n            while math.abs(timeToMax - lasttime) > 0.5 do\n                local t = (timeToMax + lasttime)/2\n                if speedchk(v(t)) then\n                    timeToMax = t\n                else\n                    lasttime = t\n                end\n            end\n        end\n        -- There is no closed form solution for distance in this case.\n        -- Numerically integrate for time t=0 to t=2*T50 (or less)\n        local lastv = initial\n        local tinc  = timeToMax/ITERATIONS\n        for step = 1, ITERATIONS do\n            local speed = v(step*tinc)\n            distanceToMax = distanceToMax + (speed+lastv)*tinc/2\n            lastv = speed\n        end\n        if timeToMax < 2*t50 then\n            return distanceToMax, timeToMax\n        end\n        initial     = lastv\n    end\n    -- At full thrust, acceleration only depends on the Lorentz factor:\n    -- v(t)' = (F/m(v)) = a*sqrt(1-v(t)^2/c^2) where a = a0+b0\n    -- -> v = c*sin((at+k1)/c)\n    -- @ t=0, v=vi: k1 = c*asin(vi/c)\n    -- -> t = (c*asin(v/c) - k1)/a\n    -- x(t)' = c*sin((at+k1)/c)\n    -- x = k2 - c^2 cos((at+k1)/c)/a\n    -- @ t=0, x=0: k2 = c^2 * cos(k1/c)/a\n    local k1       = C*math.asin(initial/C)\n    local time     = (C * math.asin(final/C) - k1)/totA\n    local k2       = C2 *math.cos(k1/C)/totA\n    local distance = k2 - C2 * math.cos((totA*time + k1)/C)/totA\n    return distance+distanceToMax, time+timeToMax\nend\n--\n-- computeTravelTime - \"relativistic\" version of t=(sqrt(2ad+v^2)-v)/a\n-- initialSpeed [in]: initial (positive) speed in meters per second\n-- acceleration [in]: constant acceleration until 'distance' is traversed\n-- distance [in]: the distance traveled in meters\n-- return: the time in seconds spent in traversing the distance\n--\nfunction Kinematic.computeTravelTime(initial, acceleration, distance)\n    -- The low speed limit of following is: t=(sqrt(2ad+v^2)-v)/a\n    -- (from: d=vt+at^2/2)\n    if distance == 0 then return 0 end\n    if acceleration > 0 then\n        local k1       = C*math.asin(initial/C)\n        local k2       = C2*math.cos(k1/C)/acceleration\n        return (C*math.acos(acceleration*(k2 - distance)/C2) - k1)/acceleration\n    end\n    assert(initial > 0, 'Acceleration and initial speed are both zero.')\n    return distance/initial\nend\nfunction Kinematic.lorentz(v) return lorentz(v) end\nreturn Kinematic\nend\nPlanetaryReference = PlanetRef()\ngalaxyReference = PlanetaryReference(Atlas())\nKinematic = Kinematics()\nKep = Keplers()\nfunction getDistanceDisplayString(distance)\n    local su = distance > 100000\n    local result = \"\"\n    if su then\n        -- Convert to SU\n        result = round(distance/1000/200,1) .. \" SU\"\n    else\n        -- Convert to KM\n        result = round(distance/1000,1) .. \" KM\"\n    end\n\n    return result\nend\n\nPlanetaryReference = PlanetRef()\ngalaxyReference = PlanetaryReference(Atlas())\n\nMapScreenButtons = {}\nMapScreenMouseX = 0\nMapScreenMouseY = 0\nMapScreenMouseDown = false\nMapScreenButtonSelected = 0\nlocal worldPos = vec3(core.getConstructWorldPos())\nlocal locX = (worldPos.x/400000)\nlocal locY = (worldPos.y/400000)*(-1)\nlocal destX = 0\nlocal destY = 0\nlocal sudistance = 0\nlocal loc = vec3(core.getConstructWorldPos())\nlocal ion = galaxyReference[0][120]  ---uses Atlas functions\nlocal thades = vec3(29165536.000, 10865536.000, 65536.000)\nlocal sinnen = vec3(58665536.000, 29665536.000, 58165536.000)\nlocal alioth = galaxyReference[0][2]    ---uses Atlas functions\nlocal madis = vec3(17465536.000, 22665536.000, -34464.000)\nlocal jago = vec3(-94134464.000, 12765536.000, -3634464.000)\nlocal symeon = vec3(14165536.000, -85634464.000, -934464.000)\nlocal lacobus = vec3(98865536.000, -13534464.000, -934464.000)\nlocal teoma = vec3(80865536.000, 54665536.000, -934464.000)\nlocal feli = vec3(-43534464.000, 22565536.000, -48934464.000)\nlocal talemai = vec3(-13234464.000, 55765536.000, 465536.000)\nlocal sicari = vec3(52765536.000, 27165536.000, 52065536.000)\nlocal distion = math.floor(ion:getDistance(loc)/200000)   ---uses getDistance functions----\nlocal distthades = string.format(\"%.2f\", math.sqrt((loc.x-thades.x)^2+(loc.y-thades.y)^2+(loc.z-thades.z)^2)/200000)\nlocal distalioth = math.floor(alioth:getDistance(loc)/200000)   ---uses getDistance functions----\nlocal distmadis = string.format(\"%.2f\", math.sqrt((loc.x-madis.x)^2+(loc.y-madis.y)^2+(loc.z-madis.z)^2)/200000)\nlocal distjago = string.format(\"%.2f\", math.sqrt((loc.x-jago.x)^2+(loc.y-jago.y)^2+(loc.z-jago.z)^2)/200000)\nlocal distlacobus = string.format(\"%.2f\", math.sqrt((loc.x-lacobus.x)^2+(loc.y-lacobus.y)^2+(loc.z-lacobus.z)^2)/200000)\nlocal distteoma = string.format(\"%.2f\", math.sqrt((loc.x-teoma.x)^2+(loc.y-teoma.y)^2+(loc.z-teoma.z)^2)/200000)\nlocal distsymeon = string.format(\"%.2f\", math.sqrt((loc.x-symeon.x)^2+(loc.y-symeon.y)^2+(loc.z-symeon.z)^2)/200000)\nlocal distfeli = string.format(\"%.2f\", math.sqrt((loc.x-feli.x)^2+(loc.y-feli.y)^2+(loc.z-feli.z)^2)/200000)\nlocal distsinnen = string.format(\"%.2f\", math.sqrt((loc.x-sinnen.x)^2+(loc.y-sinnen.y)^2+(loc.z-sinnen.z)^2)/200000)\nlocal disttalemai = string.format(\"%.2f\", math.sqrt((loc.x-talemai.x)^2+(loc.y-talemai.y)^2+(loc.z-talemai.z)^2)/200000)\nlocal distsicari = string.format(\"%.2f\", math.sqrt((loc.x-sicari.x)^2+(loc.y-sicari.y)^2+(loc.z-sicari.z)^2)/200000)\nselection = 0\n\n\n      for i = 1,1 do\n   local button = {id = (\"b\"..1), enabled=true, td=\"<td>\", top=2/100, bottom=13/100, left=1/100, right=28/100}\n    table.insert(MapScreenButtons, button)\nend\n  for i = 2,2 do\n   local button = {id = (\"b\"..2), enabled=true, td=\"<td>\", top=15/100, bottom=26/100, left=1/100, right=30/100}\n    table.insert(MapScreenButtons, button)\nend\n    for i = 3,3 do\n    local button = {id = (\"b\"..3), enabled=true, td=\"<td>\", top=27/100, bottom=38/100, left=1/100, right=28/100}\n      table.insert(MapScreenButtons, button)\nend\n    for i = 4,4 do\n    local button = {id = (\"b\"..4), enabled=true, td=\"<td>\", top=39/100, bottom=50/100, left=1/100, right=28/100}\n      table.insert(MapScreenButtons, button)\nend\n    for i = 5,5 do\n    local button = {id = (\"b\"..5), enabled=true, td=\"<td>\", top=51/100, bottom=62/100, left=1/100, right=28/100}\n      table.insert(MapScreenButtons, button)\nend\n    for i = 6,6 do\n    local button = {id = (\"b\"..6), enabled=true, td=\"<td>\", top=64/100, bottom=75/100, left=1/100, right=28/100}\n      table.insert(MapScreenButtons, button)\nend\n    for i = 7,7 do\n    local button = {id = (\"b\"..7), enabled=true, td=\"<td>\", top=2/100, bottom=13/100, left=75/100, right=100/100}\n      table.insert(MapScreenButtons, button)\nend\n    for i = 8,8 do\n    local button = {id = (\"b\"..8), enabled=true, td=\"<td>\", top=15/100, bottom=26/100, left=75/100, right=100/100}\n      table.insert(MapScreenButtons, button)\nend\n    for i = 9,9 do\n    local button = {id = (\"b\"..9), enabled=true, td=\"<td>\", top=27/100, bottom=38/100, left=75/100, right=100/100}\n      table.insert(MapScreenButtons, button)\nend\n    for i = 10,10 do\n    local button = {id = (\"b\"..10), enabled=true, td=\"<td>\", top=39/100, bottom=50/100, left=75/100, right=100/100}\n      table.insert(MapScreenButtons, button)\nend\n    for i = 11,11 do\n    local button = {id = (\"b\"..11), enabled=true, td=\"<td>\", top=51/100, bottom=62/100, left=75/100, right=100/100}\n      table.insert(MapScreenButtons, button)\nend\n    for i = 12,12 do\n    local button = {id = (\"b\"..12), enabled=true, td=\"<td>\", top=64/100, bottom=75/100, left=75/100, right=100/100}\n      table.insert(MapScreenButtons, button)\nend\n    for i = 13,13 do\n    local button = {id = (\"b\"..13), enabled=true, td=\"<td>\", top=90/100, bottom=100/100, left=1/100, right=18/100}\n    table.insert(MapScreenButtons, button)\nend\nfunction evaluateButtons()\n  local selected = 0\n\n  if #MapScreenButtons >= 1 then\n -- Set button styles\n       for i, button in ipairs(MapScreenButtons) do\n            if button.left < MapScreenMouseX and MapScreenMouseX < button.right and button.top < MapScreenMouseY and MapScreenMouseY < button.bottom then\n                if MapScreenMouseDown and MapScreenButtonSelected == i then\n                end\n                selected = i\n            end\n            if not button.enabled then\n            end\n\n        end\n  end\n  return selected\nend\n\nfunction onButtonDown(buttonNo)\n  local button = MapScreenButtons[buttonNo]\n  if not button or not button.enabled then\n\treturn\n  end\nend\nfunction onButtonUp(buttonNo)\n  local button = MapScreenButtons[buttonNo]\n  if not button or not button.enabled then\n    return\n  end\nfunction onClick(buttonNo)\n  local button = MapScreenButtons[buttonNo]\n  if not button or not button.enabled then\n    return\n  end\nend\n\n\n\n  if buttonNo == 1 then\ndestX = 0\ndestY = 0\nselection = 1\nsudistance = distalioth\n  elseif buttonNo == 2 then\ndestX = 43\ndestY = -56\nselection = 2\nsudistance = distmadis\n  elseif buttonNo == 3 then\ndestX = 73\ndestY = -27\nselection = 3\nsudistance = distthades\n  elseif buttonNo == 4 then\ndestX = -33\ndestY = -139\nselection = 4\nsudistance = disttalemai\n  elseif buttonNo == 5 then\ndestX = -109\ndestY = -56\nselection = 5\nsudistance = distfeli\n  elseif buttonNo == 6 then\ndestX = 131\ndestY = -68\nselection = 6\nsudistance = distsicari\n  elseif buttonNo == 7 then\ndestX = 35\ndestY = 214\nselection = 7\nsudistance = distsymeon\n  elseif buttonNo == 8 then\ndestX = 146\ndestY = -74\nselection = 8\nsudistance = distsinnen\n  elseif buttonNo == 9 then\ndestX = -235\ndestY = -32\nselection = 9\nsudistance = distjago\n  elseif buttonNo == 10 then\ndestX = 202\ndestY = -137\nselection = 10\nsudistance = distteoma\n  elseif buttonNo == 11 then\ndestX = 7\ndestY = 247\nselection = 11\nsudistance = distion\n  elseif buttonNo == 12 then\ndestX = 247\ndestY = 34\nselection = 12\nsudistance = distlacobus\n  elseif buttonNo == 13 then\n  unit.exit()\n  end\nend\n\n\nlocal floor = math.floor\nlocal concat = table.concat\n\nlocal secondsInMinute = 60\nlocal secondsInHour = secondsInMinute * 60\nlocal secondsInDay = secondsInHour * 24\nlocal secondsInYear = 365.2419 * secondsInDay\n\nlocal minTotalSecondsToShowOnlyYears = secondsInYear * 10\n\n---@param totalSeconds number\n---@param maxComponents nil|number\nlocal function formatTimeWithUnits (totalSeconds, maxComponents)\n  maxComponents = maxComponents or 2\n\n  local buffer = {}\n\n  if totalSeconds < 0 then\n    buffer[#buffer + 1] = \"-\"\n    totalSeconds = -totalSeconds\n    maxComponents = maxComponents + 1\n  end\n\n  local showOnlyYears = totalSeconds > minTotalSecondsToShowOnlyYears\n\n  local years = floor(totalSeconds / secondsInYear)\n  if years > 0 then buffer[#buffer + 1] = years .. \"y\" end\n\n  if #buffer < maxComponents and not showOnlyYears then\n    local days = floor(totalSeconds % secondsInYear / secondsInDay)\n    if days > 0 then buffer[#buffer + 1] = days .. \"d\" end\n  end\n\n  if #buffer < maxComponents and not showOnlyYears then\n    local hours = floor(totalSeconds % secondsInDay / secondsInHour)\n    if hours > 0 then buffer[#buffer + 1] = hours .. \"h\" end\n  end\n\n  if #buffer < maxComponents and not showOnlyYears then\n    local minutes = floor(totalSeconds % secondsInHour / secondsInMinute)\n    if minutes > 0 then buffer[#buffer + 1] = minutes .. \"m\" end\n  end\n\n  if #buffer < maxComponents and not showOnlyYears then\n    local seconds = floor(totalSeconds % secondsInMinute)\n    if seconds > 0 then buffer[#buffer + 1] = seconds .. \"s\" end\n  end\n\n  if #buffer == 0 then return \"0s\" end\n\n  return concat(buffer, \" \")\n\nend\n\n\n\n\nfunction updateScreen()\n\nloc = vec3(core.getConstructWorldPos())\n\nlocal shipVelocity = vec3(core.getVelocity()):len() * 3.6\nlocal shipAcceleration = vec3(core.getVelocity()):len() * 3.6\nlocal time_to_distance = 0\nlocal display_selection = \"\"\n\nif selection == 1 then\n    alioth = galaxyReference[0][2]    ---uses Atlas functions\n    distalioth = math.floor(alioth:getDistance(loc)/200000)   ---uses getDistance functions----\n    time_to_distance = distalioth * 200 / shipVelocity\n    display_selection = \"alioth\"\nelseif selection == 2 then\n    madis = vec3(17465536.000, 22665536.000, -34464.000)\n    distmadis = string.format(\"%.2f\", math.sqrt((loc.x-madis.x)^2+(loc.y-madis.y)^2+(loc.z-madis.z)^2)/200000)\n    time_to_distance = distmadis * 200 / shipVelocity\n    display_selection = \"madis\"\nelseif selection == 3 then\n    thades = vec3(29165536.000, 10865536.000, 65536.000)\n    distthades = string.format(\"%.2f\", math.sqrt((loc.x-thades.x)^2+(loc.y-thades.y)^2+(loc.z-thades.z)^2)/200000)\n    time_to_distance = distthades * 200 / shipVelocity\n    display_selection = \"thades\"\nelseif selection == 4 then\n    talemai = vec3(-13234464.000, 55765536.000, 465536.000)\n    disttalemai = string.format(\"%.2f\", math.sqrt((loc.x-talemai.x)^2+(loc.y-talemai.y)^2+(loc.z-talemai.z)^2)/200000)\n    time_to_distance = disttalemai * 200 / shipVelocity\n    display_selection = \"talemai\"\nelseif selection == 5 then\n    feli = vec3(-43534464.000, 22565536.000, -48934464.000)\n    distfeli = string.format(\"%.2f\", math.sqrt((loc.x-feli.x)^2+(loc.y-feli.y)^2+(loc.z-feli.z)^2)/200000)\n    time_to_distance = distfeli * 200 / shipVelocity\n    display_selection = \"feli\"\nelseif selection == 6 then\n    sicari = vec3(52765536.000, 27165536.000, 52065536.000)\n    distsicari = string.format(\"%.2f\", math.sqrt((loc.x-sicari.x)^2+(loc.y-sicari.y)^2+(loc.z-sicari.z)^2)/200000)\n    time_to_distance = distsicari * 200 / shipVelocity\n    display_selection = \"sicari\"\nelseif selection == 7 then\n    symeon = vec3(14165536.000, -85634464.000, -934464.000)\n    distsymeon = string.format(\"%.2f\", math.sqrt((loc.x-symeon.x)^2+(loc.y-symeon.y)^2+(loc.z-symeon.z)^2)/200000)\n    time_to_distance = distsymeon * 200 / shipVelocity\n    display_selection = \"symeon\"\nelseif selection == 8 then\n    sinnen = vec3(58665536.000, 29665536.000, 58165536.000)\n    distsinnen = string.format(\"%.2f\", math.sqrt((loc.x-sinnen.x)^2+(loc.y-sinnen.y)^2+(loc.z-sinnen.z)^2)/200000)\n    time_to_distance = distsinnen * 200 / shipVelocity\n    display_selection = \"sinnen\"\nelseif selection == 9 then\n    jago = vec3(-94134464.000, 12765536.000, -3634464.000)\n    distjago = string.format(\"%.2f\", math.sqrt((loc.x-jago.x)^2+(loc.y-jago.y)^2+(loc.z-jago.z)^2)/200000)\n    time_to_distance = distjago * 200 / shipVelocity\n    display_selection = \"jago\"\nelseif selection == 10 then\n    teoma = vec3(80865536.000, 54665536.000, -934464.000)\n    distteoma = string.format(\"%.2f\", math.sqrt((loc.x-teoma.x)^2+(loc.y-teoma.y)^2+(loc.z-teoma.z)^2)/200000)\n    time_to_distance = distteoma * 200 / shipVelocity\n    display_selection = \"teoma\"\nelseif selection == 11 then\n    ion = galaxyReference[0][120]  ---uses Atlas functions\n    distion = math.floor(ion:getDistance(loc)/200000)   ---uses getDistance functions----\n    time_to_distance = distion * 200 / shipVelocity\n    display_selection = \"ion\"\nelseif selection == 12 then\n    lacobus = vec3(98865536.000, -13534464.000, -934464.000)\n    distlacobus = string.format(\"%.2f\", math.sqrt((loc.x-lacobus.x)^2+(loc.y-lacobus.y)^2+(loc.z-lacobus.z)^2)/200000)\n    time_to_distance = distlacobus * 200 / shipVelocity\n    display_selection = \"lacobus\"\nelse\n    time_to_distance = sudistance * 200 / shipVelocity\n    display_selection = \"none\"\nend\nshipVelocity = string.format(\"%.2f\", shipVelocity)\nwarpmath = math.floor(math.floor(core.getConstructMass()/ 1000)  * sudistance * 0.00025)\n\ntime_to_distance_min = time_to_distance * 60\ntime_to_distance_min_sec = time_to_distance_min * 60\n\ntime_to_distance = formatTimeWithUnits (time_to_distance_min_sec, 3)\n\nhtml= ([[\n<svg class=\"bootstrap\" viewBox=\"0 0 1024 1024\" style=\"width:100%; height:100%\">\n\n<circle cx=\"500\" cy=\"500\" r=\"400\" stroke=\"#FFF\" stroke-width=\"3\" transform=\"\"></circle>\n<circle cx=\"500\" cy=\"500\" r=\"350\" stroke=\"#FFF\" stroke-width=\"3\" transform=\"\" stroke-opacity=\"0.2\"></circle>\n<circle cx=\"500\" cy=\"500\" r=\"300\" stroke=\"#FFF\" stroke-width=\"3\" transform=\"\"></circle><circle cx=\"500\" cy=\"500\" r=\"250\" stroke=\"#FFF\" stroke-width=\"3\" transform=\"\" stroke-opacity=\"0.2\"></circle>\n<circle cx=\"500\" cy=\"500\" r=\"200\" stroke=\"#FFF\" stroke-width=\"3\" transform=\"\"></circle><circle cx=\"500\" cy=\"500\" r=\"150\" stroke=\"#FFF\" stroke-width=\"3\" transform=\"\" stroke-opacity=\"0.2\"></circle>\n<circle cx=\"500\" cy=\"500\" r=\"100\" stroke=\"lightblue\" stroke-width=\"3\" transform=\"\"></circle><circle cx=\"500\" cy=\"500\" r=\"50\" stroke=\"lightblue\" stroke-width=\"3\" transform=\"\" stroke-opacity=\"0.2\"></circle>\n<circle cx=\"500\" cy=\"500\" r=\"20\" stroke=\"Orange\" stroke-width=\"2\" transform=\"\"></circle><text x=\"510\" y=\"510\" fill=\"Yellow\">Helios</text>\n<circle cx=\"-0.00\" cy=\"0\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle>\n<text x=\"-0.00\" y=\"0\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Alioth</text><circle cx=\"7.16\" cy=\"247.59\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle>\n<text x=\"7.16\" y=\"247.59\" transform=\"translate(480,480)\" fill=\"white\" font-size=\"20\">Ion</text><circle cx=\"35.41\" cy=\"214.09\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle>\n<text x=\"35.41\" y=\"214.09\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Symeon</text><circle cx=\"-33.09\" cy=\"-139.41\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle>\n<text x=\"-33.09\" y=\"-139.41\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Talemai</text><circle cx=\"202.16\" cy=\"-136.66\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle>\n<text x=\"202.16\" y=\"-136.66\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Teoma</text><circle cx=\"247.16\" cy=\"33.84\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle>\n<text x=\"247.16\" y=\"33.84\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Lacobus</text><circle cx=\"-108.84\" cy=\"-56.41\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle>\n<text x=\"-108.84\" y=\"-56.41\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Feli</text><circle cx=\"72.91\" cy=\"-27.16\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle>\n<text x=\"72.91\" y=\"-27.16\" transform=\"translate(500,485)\" fill=\"white\" font-size=\"20\">Thades</text><circle cx=\"43.66\" cy=\"-56.66\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle>\n<text x=\"43.66\" y=\"-56.66\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Madis</text><circle cx=\"-235.34\" cy=\"-31.91\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle>\n<text x=\"-235.34\" y=\"-31.91\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Jago</text><circle cx=\"131.91\" cy=\"-67.91\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle>\n<text x=\"131.91\" y=\"-67.91\" transform=\"translate(475,480)\" fill=\"white\" font-size=\"20\">Sicari</text><circle cx=\"146.66\" cy=\"-74.16\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle>\n<text x=\"146.66\" y=\"-74.16\" transform=\"translate(515,480)\" fill=\"white\" font-size=\"20\">Sinnen</text>\n<line stroke-linecap=\"undefined\" stroke-linejoin=\"undefined\" id=\"svg_1\" y2=\"]]..destY..[[\" x2=\"]]..destX..[[\" y1=\"]]..locY..[[\" x1=\"]]..locX..[[\" transform=\"translate(500,500)\" stroke-width=\"5\" stroke=\"#ff0000\" fill=\"none\"/>\n<circle cx=\"]]..locX..[[\" cy=\"]]..locY..[[\" r=\"3\" stroke=\"black\" stroke-width=\"1\" fill=\"limegreen\" transform=\"translate(500,500)\"></circle>\n<text x=\"]]..locX..[[\" y=\"]]..locY..[[\" transform=\"translate(500,500)\"\nfill=\"limegreen\" font-size= \"4.5vh\" font-weight= \"bold\">//SHIP POSITION</text>\n</svg>\n<svg class=\"bootstrap\" viewBox=\"0 0 1024 612\" style=\"width:100%; height:100%\">\n\n <g>\n  <title>Layer 1</title>\n  <g id=\"svg_24\">\n   <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_8\" y=\"70\" x=\"55\" stroke-width=\"0\" fill=\"Yellow\">Alioth  :]]..distalioth..[[ SU</text>\n   <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_14\" y=\"170\" x=\"55\" stroke-width=\"0\" fill=\"Yellow\">Madis  :]]..distmadis..[[ SU</text>\n   <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_17\" y=\"270\" x=\"55\" stroke-width=\"0\" fill=\"Yellow\">Thades  :]]..distthades..[[ SU</text>\n   <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_20\" y=\"370\" x=\"55\" stroke-width=\"0\" fill=\"Yellow\">Talemai  :]]..disttalemai..[[ SU</text>\n   <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_23\" y=\"470\" x=\"55\" stroke-width=\"0\" fill=\"Yellow\">Feli  :]]..distfeli..[[ SU</text>\n   <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_26\" y=\"570\" x=\"55\" stroke-width=\"0\" fill=\"Yellow\">Sicari  :]]..distsicari..[[ SU</text>\n   <g id=\"svg_12\">\n    <rect rx=\"10\" id=\"svg_1\" height=\"50\" width=\"250\" y=\"30\" x=\"15\" stroke-width=\"10\" stroke=\"#FFF\" fill=\"none\"/>\n    <rect rx=\"10\" id=\"svg_3\" height=\"50\" width=\"250\" y=\"105\" x=\"15\" stroke-width=\"10\" stroke=\"#FFF\" fill=\"none\"/>\n    <rect rx=\"10\" id=\"svg_7\" height=\"50\" width=\"250\" y=\"180\" x=\"15\" stroke-width=\"10\" stroke=\"#FFF\" fill=\"none\"/>\n    <rect rx=\"10\" id=\"svg_9\" height=\"50\" width=\"250\" y=\"255\" x=\"15\" stroke-width=\"10\" stroke=\"#FFF\" fill=\"none\"/>\n    <rect rx=\"10\" id=\"svg_10\" height=\"50\" width=\"250\" y=\"330\" x=\"15\" stroke-width=\"10\" stroke=\"#FFF\" fill=\"none\"/>\n    <rect rx=\"10\" id=\"svg_11\" height=\"50\" width=\"250\" y=\"405\" x=\"15\" stroke-width=\"10\" stroke=\"#FFF\" fill=\"none\"/>\n   </g>\n  </g>\n  <g id=\"svg_40\">\n   <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_25\" y=\"70\" x=\"997.163642\" stroke-width=\"0\" fill=\"Yellow\">Symeon  :]]..distsymeon..[[ SU</text>\n   <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_27\" y=\"170\" x=\"997.163642\" stroke-width=\"0\" fill=\"Yellow\">Sinnen  :]]..distsinnen..[[ SU</text>\n   <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_28\" y=\"270\" x=\"997.163642\" stroke-width=\"0\" fill=\"Yellow\">Jago  :]]..distjago..[[ SU</text>\n   <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_30\" y=\"370\" x=\"997.163642\" stroke-width=\"0\" fill=\"Yellow\">Teoma  :]]..distteoma..[[ SU</text>\n   <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_31\" y=\"470\" x=\"997.163642\" stroke-width=\"0\" fill=\"Yellow\">Ion  :]]..distion..[[ SU</text>\n   <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_32\" y=\"570\" x=\"997.163642\" stroke-width=\"0\" fill=\"Yellow\">Lacobus  :]]..distlacobus..[[ SU</text>\n   <g id=\"svg_39\">\n    <rect rx=\"10\" id=\"svg_33\" height=\"50\" width=\"250\" y=\"30\" x=\"760\" stroke-width=\"10\" stroke=\"#FFF\" fill=\"none\"/>\n    <rect rx=\"10\" id=\"svg_34\" height=\"50\" width=\"250\" y=\"105\" x=\"760\" stroke-width=\"10\" stroke=\"#FFF\" fill=\"none\"/>\n    <rect rx=\"10\" id=\"svg_35\" height=\"50\" width=\"250\" y=\"180\" x=\"760\" stroke-width=\"10\" stroke=\"#FFF\" fill=\"none\"/>\n    <rect rx=\"10\" id=\"svg_36\" height=\"50\" width=\"250\" y=\"255\" x=\"760\" stroke-width=\"10\" stroke=\"#FFF\" fill=\"none\"/>\n    <rect rx=\"10\" id=\"svg_37\" height=\"50\" width=\"250\" y=\"330\" x=\"760\" stroke-width=\"10\" stroke=\"#FFF\" fill=\"none\"/>\n    <rect rx=\"10\" id=\"svg_38\" height=\"50\" width=\"250\" y=\"405\" x=\"760\" stroke-width=\"10\" stroke=\"#FFF\" fill=\"none\"/>\n   </g>\n  </g>\n </g>\n  <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"30\" id=\"svg_32\" y=\"700\" x=\"20\" stroke-width=\"0\" fill=\"LightBlue\">Est. Warp Cost: ]]..warpmath..[[</text>\n  <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"30\" id=\"svg_32\" y=\"750\" x=\"20\" stroke-width=\"0\" fill=\"LightBlue\">Construct Weight: ]]..math.floor(core.getConstructMass()/ 1000)..[[ tons</text>\n   <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"30\" id=\"svg_32\" y=\"700\" x=\"500\" stroke-width=\"0\" fill=\"LightBlue\">TTD: ]]..time_to_distance..[[ </text>\n   <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"30\" id=\"svg_32\" y=\"750\" x=\"500\" stroke-width=\"0\" fill=\"LightBlue\">VELOC: ]]..shipVelocity..[[ km/h</text>\n   <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"30\" id=\"svg_32\" y=\"700\" x=\"900\" stroke-width=\"0\" fill=\"LightBlue\">DEST: ]]..display_selection..[[ </text>\n\n</svg>\n\n    ]])\n\nscreen.setHTML(html)\n--screen2.setHTML(html)\nend\nunit.setTimer(\"spacemap\",.08)\n\n","filter":{"args":[],"signature":"start()","slotKey":"-1"},"key":"2"},{"code":"updateScreen()","filter":{"args":[{"value":"spacemap"}],"signature":"tick(timerId)","slotKey":"-1"},"key":"3"}],"methods":[],"events":[]}





----Older Version In Multiple formats for Learning Purposes. DU IT :)
Space Map with Warp Calculation

Credits to All of DU Lua Community


Requirements:
1 Dynamic Construct
1 Screen
1 Program Board

Link Screen then Core

Two Versions are available and do the same thing.

One uses variables and math with some built in functions from Lua to get the values.
One uses Functions from JayleBreak's atlas and functions to get the values.

 


*KNOWN BUGS*

----On fresh login, Core has to be ran one time to send Construct weight data over the server, otherwise math will be wrong.
So sit down in pilot seat one time and then run programming board. After that its always set. ---Server Bug


----Currently has Bug with Screen Flickers on half of the svg during setHTML redraw update-- 9/23 Patch

 

 


Planets are organized similar to DU ingame map and are stationary svg.
---------- Math and Variables Version---------

---Space Map
---Unit Start
MapScreenButtons = {}
MapScreenMouseX = 0
MapScreenMouseY = 0
MapScreenMouseDown = false
MapScreenButtonSelected = 0



local worldPos = vec3(core.getConstructWorldPos())
local locX = (worldPos.x/400000)
local locY = (worldPos.y/400000)*(-1)
local destX = 0
local destY = 0
local sudistance = 0
local loc = vec3(core.getConstructWorldPos())
local ion = vec3(2865536.000, -99034464.000, -934464.000)
local thades = vec3(29165536.000, 10865536.000, 65536.000)
local sinnen = vec3(58665536.000, 29665536.000, 58165536.000)
local alioth = vec3(-8.000, -8.000, -126303.000)
local madis = vec3(17465536.000, 22665536.000, -34464.000)
local jago = vec3(-94134464.000, 12765536.000, -3634464.000)
local symeon = vec3(14165536.000, -85634464.000, -934464.000)
local lacobus = vec3(98865536.000, -13534464.000, -934464.000)
local teoma = vec3(80865536.000, 54665536.000, -934464.000)
local feli = vec3(-43534464.000, 22565536.000, -48934464.000)
local talemai = vec3(-13234464.000, 55765536.000, 465536.000)
local sicari = vec3(52765536.000, 27165536.000, 52065536.000)
local distion = string.format("%.2f", math.sqrt((loc.x-ion.x)^2+(loc.y-ion.y)^2+(loc.z-ion.z)^2)/200000)
local distthades = string.format("%.2f", math.sqrt((loc.x-thades.x)^2+(loc.y-thades.y)^2+(loc.z-thades.z)^2)/200000)
local distalioth = string.format("%.2f", math.sqrt((loc.x-alioth.x)^2+(loc.y-alioth.y)^2+(loc.z-alioth.z)^2)/200000)
local distmadis = string.format("%.2f", math.sqrt((loc.x-madis.x)^2+(loc.y-madis.y)^2+(loc.z-madis.z)^2)/200000)
local distjago = string.format("%.2f", math.sqrt((loc.x-jago.x)^2+(loc.y-jago.y)^2+(loc.z-jago.z)^2)/200000)
local distlacobus = string.format("%.2f", math.sqrt((loc.x-lacobus.x)^2+(loc.y-lacobus.y)^2+(loc.z-lacobus.z)^2)/200000)
local distteoma = string.format("%.2f", math.sqrt((loc.x-teoma.x)^2+(loc.y-teoma.y)^2+(loc.z-teoma.z)^2)/200000)
local distsymeon = string.format("%.2f", math.sqrt((loc.x-symeon.x)^2+(loc.y-symeon.y)^2+(loc.z-symeon.z)^2)/200000)
local distfeli = string.format("%.2f", math.sqrt((loc.x-feli.x)^2+(loc.y-feli.y)^2+(loc.z-feli.z)^2)/200000)
local distsinnen = string.format("%.2f", math.sqrt((loc.x-sinnen.x)^2+(loc.y-sinnen.y)^2+(loc.z-sinnen.z)^2)/200000)
local disttalemai = string.format("%.2f", math.sqrt((loc.x-talemai.x)^2+(loc.y-talemai.y)^2+(loc.z-talemai.z)^2)/200000)
local distsicari = string.format("%.2f", math.sqrt((loc.x-sicari.x)^2+(loc.y-sicari.y)^2+(loc.z-sicari.z)^2)/200000)



      for i = 1,1 do
   local button = {id = ("b"..1), enabled=true, td="<td>", top=2/100, bottom=13/100, left=1/100, right=28/100}
    table.insert(MapScreenButtons, button)
end
  for i = 2,2 do
   local button = {id = ("b"..2), enabled=true, td="<td>", top=15/100, bottom=26/100, left=1/100, right=30/100}
    table.insert(MapScreenButtons, button)
end
    for i = 3,3 do
    local button = {id = ("b"..3), enabled=true, td="<td>", top=27/100, bottom=38/100, left=1/100, right=28/100}
      table.insert(MapScreenButtons, button)
end
    for i = 4,4 do   
    local button = {id = ("b"..4), enabled=true, td="<td>", top=39/100, bottom=50/100, left=1/100, right=28/100}
      table.insert(MapScreenButtons, button)
end
    for i = 5,5 do   
    local button = {id = ("b"..5), enabled=true, td="<td>", top=51/100, bottom=62/100, left=1/100, right=28/100}
      table.insert(MapScreenButtons, button)
end
    for i = 6,6 do   
    local button = {id = ("b"..6), enabled=true, td="<td>", top=64/100, bottom=75/100, left=1/100, right=28/100}
      table.insert(MapScreenButtons, button)
end
    for i = 7,7 do   
    local button = {id = ("b"..7), enabled=true, td="<td>", top=2/100, bottom=13/100, left=75/100, right=100/100}
      table.insert(MapScreenButtons, button)
end
    for i = 8,8 do   
    local button = {id = ("b"..8), enabled=true, td="<td>", top=15/100, bottom=26/100, left=75/100, right=100/100}
      table.insert(MapScreenButtons, button)
end
    for i = 9,9 do   
    local button = {id = ("b"..9), enabled=true, td="<td>", top=27/100, bottom=38/100, left=75/100, right=100/100}
      table.insert(MapScreenButtons, button)
end
    for i = 10,10 do   
    local button = {id = ("b"..10), enabled=true, td="<td>", top=39/100, bottom=50/100, left=75/100, right=100/100}
      table.insert(MapScreenButtons, button)
end
    for i = 11,11 do   
    local button = {id = ("b"..11), enabled=true, td="<td>", top=51/100, bottom=62/100, left=75/100, right=100/100}
      table.insert(MapScreenButtons, button)
end
    for i = 12,12 do   
    local button = {id = ("b"..12), enabled=true, td="<td>", top=64/100, bottom=75/100, left=75/100, right=100/100}
      table.insert(MapScreenButtons, button)
end
    for i = 13,13 do   
    local button = {id = ("b"..13), enabled=true, td="<td>", top=90/100, bottom=100/100, left=1/100, right=18/100}
    table.insert(MapScreenButtons, button)
end
function evaluateButtons()
  local selected = 0
  
  if #MapScreenButtons >= 1 then
 -- Set button styles
       for i, button in ipairs(MapScreenButtons) do
            if button.left < MapScreenMouseX and MapScreenMouseX < button.right and button.top < MapScreenMouseY and MapScreenMouseY < button.bottom then
                if MapScreenMouseDown and MapScreenButtonSelected == i then
                end
                selected = i
            end
            if not button.enabled then
            end

        end
  end
  return selected
end

function onButtonDown(buttonNo)
  local button = MapScreenButtons[buttonNo]    
  if not button or not button.enabled then
	return
  end
end
function onButtonUp(buttonNo)
  local button = MapScreenButtons[buttonNo]    
  if not button or not button.enabled then
    return
  end
function onClick(buttonNo)
  local button = MapScreenButtons[buttonNo]    
  if not button or not button.enabled then
    return
  end
end
  if buttonNo == 1 then
destX = 0
destY = 0
sudistance = distalioth
  elseif buttonNo == 2 then
destX = 43
destY = -56
sudistance = distmadis
  elseif buttonNo == 3 then
destX = 73
destY = -27
sudistance = distthades        
  elseif buttonNo == 4 then
destX = -33
destY = -139
sudistance = disttalemai
  elseif buttonNo == 5 then
destX = -109
destY = -56
sudistance = distfeli        
  elseif buttonNo == 6 then
destX = 131
destY = -68
sudistance = distsicari        
  elseif buttonNo == 7 then
destX = 35
destY = 214
sudistance = distsymeon
  elseif buttonNo == 8 then
destX = 146
destY = -74
sudistance = distsinnen        
  elseif buttonNo == 9 then
destX = -235
destY = -32
sudistance = distjago       
  elseif buttonNo == 10 then
destX = 202
destY = -137
sudistance = distteoma        
  elseif buttonNo == 11 then
destX = 7
destY = 247
sudistance = distion        
  elseif buttonNo == 12 then
destX = 247
destY = 34
sudistance = distlacobus
  elseif buttonNo == 13 then
  unit.exit()
  end
end

function updateScreen() 
warpmath = math.floor(math.floor(core.getConstructMass()/ 1000)  * sudistance * 0.00025)
html= ([[
<svg class="bootstrap" viewBox="0 0 1024 1024" style="width:100%; height:100%">      
<circle cx="500" cy="500" r="400" stroke="darkgreen" stroke-width="3" transform=""></circle><circle cx="500" cy="500" r="350" stroke="darkgreen" stroke-width="3" transform="" stroke-opacity="0.2"></circle><circle cx="500" cy="500" r="300" stroke="darkgreen" stroke-width="3" transform=""></circle><circle cx="500" cy="500" r="250" stroke="darkgreen" stroke-width="3" transform="" stroke-opacity="0.2"></circle><circle cx="500" cy="500" r="200" stroke="darkgreen" stroke-width="3" transform=""></circle><circle cx="500" cy="500" r="150" stroke="darkgreen" stroke-width="3" transform="" stroke-opacity="0.2"></circle><circle cx="500" cy="500" r="100" stroke="lightblue" stroke-width="3" transform=""></circle><circle cx="500" cy="500" r="50" stroke="lightblue" stroke-width="3" transform="" stroke-opacity="0.2"></circle><circle cx="500" cy="500" r="20" stroke="Orange" stroke-width="2" transform=""></circle><text x="510" y="510" fill="Yellow">Helios</text><circle cx="-0.00" cy="0" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="-0.00" y="0" transform="translate(500,480)" fill="white" font-size="20">Alioth</text><circle cx="7.16" cy="247.59" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="7.16" y="247.59" transform="translate(480,480)" fill="white" font-size="20">Ion</text><circle cx="35.41" cy="214.09" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="35.41" y="214.09" transform="translate(500,480)" fill="white" font-size="20">Symeon</text><circle cx="-33.09" cy="-139.41" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="-33.09" y="-139.41" transform="translate(500,480)" fill="white" font-size="20">Talemai</text><circle cx="202.16" cy="-136.66" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="202.16" y="-136.66" transform="translate(500,480)" fill="white" font-size="20">Teoma</text><circle cx="247.16" cy="33.84" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="247.16" y="33.84" transform="translate(500,480)" fill="white" font-size="20">Lacobus</text><circle cx="-108.84" cy="-56.41" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="-108.84" y="-56.41" transform="translate(500,480)" fill="white" font-size="20">Feli</text><circle cx="72.91" cy="-27.16" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="72.91" y="-27.16" transform="translate(500,485)" fill="white" font-size="20">Thades</text><circle cx="43.66" cy="-56.66" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="43.66" y="-56.66" transform="translate(500,480)" fill="white" font-size="20">Madis</text><circle cx="-235.34" cy="-31.91" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="-235.34" y="-31.91" transform="translate(500,480)" fill="white" font-size="20">Jago</text><circle cx="131.91" cy="-67.91" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="131.91" y="-67.91" transform="translate(475,480)" fill="white" font-size="20">Sicari</text><circle cx="146.66" cy="-74.16" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="146.66" y="-74.16" transform="translate(515,480)" fill="white" font-size="20">Sinnen</text>
<line stroke-linecap="undefined" stroke-linejoin="undefined" id="svg_1" y2="]]..destY..[[" x2="]]..destX..[[" y1="]]..locY..[[" x1="]]..locX..[[" transform="translate(500,500)" stroke-width="5" stroke="#ff0000" fill="none"/>        
<circle cx="]]..locX..[[" cy="]]..locY..[[" r="3" stroke="black" stroke-width="1" fill="limegreen" transform="translate(500,500)"></circle>
<text x="]]..locX..[[" y="]]..locY..[[" transform="translate(500,500)" 
fill="limegreen" font-size= "4.5vh" font-weight= "bold">//SHIP POSITION</text>
</svg>
<svg class="bootstrap" viewBox="0 0 1024 612" style="width:100%; height:100%">
 <g>
  <title>Layer 1</title>
  <g id="svg_24">
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_8" y="70" x="55" stroke-width="0" fill="Yellow">Alioth  :]]..distalioth..[[ SU</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_14" y="170" x="55" stroke-width="0" fill="Yellow">Madis  :]]..distmadis..[[ SU</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_17" y="270" x="55" stroke-width="0" fill="Yellow">Thades  :]]..distthades..[[ SU</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_20" y="370" x="55" stroke-width="0" fill="Yellow">Talemai  :]]..disttalemai..[[ SU</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_23" y="470" x="55" stroke-width="0" fill="Yellow">Feli  :]]..distfeli..[[ SU</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_26" y="570" x="55" stroke-width="0" fill="Yellow">Sicari  :]]..distsicari..[[ SU</text>
   <g id="svg_12">
    <rect rx="10" id="svg_1" height="50" width="250" y="30" x="15" stroke-width="20" stroke="#00ff00" fill="none"/>
    <rect rx="10" id="svg_3" height="50" width="250" y="105" x="15" stroke-width="20" stroke="#00ff00" fill="none"/>
    <rect rx="10" id="svg_7" height="50" width="250" y="180" x="15" stroke-width="20" stroke="#00ff00" fill="none"/>
    <rect rx="10" id="svg_9" height="50" width="250" y="255" x="15" stroke-width="20" stroke="#00ff00" fill="none"/>
    <rect rx="10" id="svg_10" height="50" width="250" y="330" x="15" stroke-width="20" stroke="#00ff00" fill="none"/>
    <rect rx="10" id="svg_11" height="50" width="250" y="405" x="15" stroke-width="20" stroke="#00ff00" fill="none"/>
   </g>
  </g>
  <g id="svg_40">
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_25" y="70" x="997.163642" stroke-width="0" fill="Yellow">Symeon  :]]..distsymeon..[[ SU</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_27" y="170" x="997.163642" stroke-width="0" fill="Yellow">Sinnen  :]]..distsinnen..[[ SU</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_28" y="270" x="997.163642" stroke-width="0" fill="Yellow">Jago  :]]..distjago..[[ SU</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_30" y="370" x="997.163642" stroke-width="0" fill="Yellow">Teoma  :]]..distteoma..[[ SU</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_31" y="470" x="997.163642" stroke-width="0" fill="Yellow">Ion  :]]..distion..[[ SU</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_32" y="570" x="997.163642" stroke-width="0" fill="Yellow">Lacobus  :]]..distlacobus..[[ SU</text>
   <g id="svg_39">
    <rect rx="10" id="svg_33" height="50" width="250" y="30" x="760" stroke-width="20" stroke="#00ff00" fill="none"/>
    <rect rx="10" id="svg_34" height="50" width="250" y="105" x="760" stroke-width="20" stroke="#00ff00" fill="none"/>
    <rect rx="10" id="svg_35" height="50" width="250" y="180" x="760" stroke-width="20" stroke="#00ff00" fill="none"/>
    <rect rx="10" id="svg_36" height="50" width="250" y="255" x="760" stroke-width="20" stroke="#00ff00" fill="none"/>
    <rect rx="10" id="svg_37" height="50" width="250" y="330" x="760" stroke-width="20" stroke="#00ff00" fill="none"/>
    <rect rx="10" id="svg_38" height="50" width="250" y="405" x="760" stroke-width="20" stroke="#00ff00" fill="none"/>
   </g>
  </g>
 </g>
<text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="30" id="svg_32" y="700" x="20" stroke-width="0" fill="LightBlue">Est. Warp Cost: ]]..warpmath..[[</text>
  <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="30" id="svg_32" y="750" x="20" stroke-width="0" fill="LightBlue">Construct Weight: ]]..math.floor(core.getConstructMass()/ 1000)..[[ tons</text>
    
</svg> 
    ]])
screen.setHTML(html)
end
unit.setTimer("spacemap",.08)







---Unit Tick spacemap
updateScreen()

---MouseDown *,*
MapScreenMouseX = x
MapScreenMouseY = y
MapScreenMouseDown = true
MapScreenButtonSelected = evaluateButtons()
onButtonDown(MapScreenButtonSelected)


---MouseUp *,*
MapScreenMouseX = x
MapScreenMouseY = y
MapScreenMouseDown = false
local buttonNo = evaluateButtons()
if MapScreenButtonSelected > 0 and MapScreenButtonSelected == buttonNo then
    onButtonUp(buttonNo)
    onClick(buttonNo)
end
MapScreenButtonSelected = -buttonNo

Json Paste
 

{"slots":{"0":{"name":"screen","type":{"events":[],"methods":[]}},"1":{"name":"core","type":{"events":[],"methods":[]}},"2":{"name":"slot3","type":{"events":[],"methods":[]}},"3":{"name":"slot4","type":{"events":[],"methods":[]}},"4":{"name":"slot5","type":{"events":[],"methods":[]}},"5":{"name":"slot6","type":{"events":[],"methods":[]}},"6":{"name":"slot7","type":{"events":[],"methods":[]}},"7":{"name":"slot8","type":{"events":[],"methods":[]}},"8":{"name":"slot9","type":{"events":[],"methods":[]}},"9":{"name":"slot10","type":{"events":[],"methods":[]}},"-1":{"name":"unit","type":{"events":[],"methods":[]}},"-2":{"name":"system","type":{"events":[],"methods":[]}},"-3":{"name":"library","type":{"events":[],"methods":[]}}},"handlers":[{"code":"MapScreenMouseX = x\nMapScreenMouseY = y\nMapScreenMouseDown = false\nlocal buttonNo = evaluateButtons()\nif MapScreenButtonSelected > 0 and MapScreenButtonSelected == buttonNo then\n    onButtonUp(buttonNo)\n    onClick(buttonNo)\nend\nMapScreenButtonSelected = -buttonNo","filter":{"args":[{"variable":"*"},{"variable":"*"}],"signature":"mouseUp(x,y)","slotKey":"0"},"key":"0"},{"code":"MapScreenMouseX = x\nMapScreenMouseY = y\nMapScreenMouseDown = true\nMapScreenButtonSelected = evaluateButtons()\nonButtonDown(MapScreenButtonSelected)\n","filter":{"args":[{"variable":"*"},{"variable":"*"}],"signature":"mouseDown(x,y)","slotKey":"0"},"key":"1"},{"code":"MapScreenButtons = {}\nMapScreenMouseX = 0\nMapScreenMouseY = 0\nMapScreenMouseDown = false\nMapScreenButtonSelected = 0\n\n\n\nlocal worldPos = vec3(core.getConstructWorldPos())\nlocal locX = (worldPos.x/400000)\nlocal locY = (worldPos.y/400000)*(-1)\nlocal destX = 0\nlocal destY = 0\nlocal sudistance = 0\nlocal loc = vec3(core.getConstructWorldPos())\nlocal ion = vec3(2865536.000, -99034464.000, -934464.000)\nlocal thades = vec3(29165536.000, 10865536.000, 65536.000)\nlocal sinnen = vec3(58665536.000, 29665536.000, 58165536.000)\nlocal alioth = vec3(-8.000, -8.000, -126303.000)\nlocal madis = vec3(17465536.000, 22665536.000, -34464.000)\nlocal jago = vec3(-94134464.000, 12765536.000, -3634464.000)\nlocal symeon = vec3(14165536.000, -85634464.000, -934464.000)\nlocal lacobus = vec3(98865536.000, -13534464.000, -934464.000)\nlocal teoma = vec3(80865536.000, 54665536.000, -934464.000)\nlocal feli = vec3(-43534464.000, 22565536.000, -48934464.000)\nlocal talemai = vec3(-13234464.000, 55765536.000, 465536.000)\nlocal sicari = vec3(52765536.000, 27165536.000, 52065536.000)\nlocal distion = string.format(\"%.2f\", math.sqrt((loc.x-ion.x)^2+(loc.y-ion.y)^2+(loc.z-ion.z)^2)/200000)\nlocal distthades = string.format(\"%.2f\", math.sqrt((loc.x-thades.x)^2+(loc.y-thades.y)^2+(loc.z-thades.z)^2)/200000)\nlocal distalioth = string.format(\"%.2f\", math.sqrt((loc.x-alioth.x)^2+(loc.y-alioth.y)^2+(loc.z-alioth.z)^2)/200000)\nlocal distmadis = string.format(\"%.2f\", math.sqrt((loc.x-madis.x)^2+(loc.y-madis.y)^2+(loc.z-madis.z)^2)/200000)\nlocal distjago = string.format(\"%.2f\", math.sqrt((loc.x-jago.x)^2+(loc.y-jago.y)^2+(loc.z-jago.z)^2)/200000)\nlocal distlacobus = string.format(\"%.2f\", math.sqrt((loc.x-lacobus.x)^2+(loc.y-lacobus.y)^2+(loc.z-lacobus.z)^2)/200000)\nlocal distteoma = string.format(\"%.2f\", math.sqrt((loc.x-teoma.x)^2+(loc.y-teoma.y)^2+(loc.z-teoma.z)^2)/200000)\nlocal distsymeon = string.format(\"%.2f\", math.sqrt((loc.x-symeon.x)^2+(loc.y-symeon.y)^2+(loc.z-symeon.z)^2)/200000)\nlocal distfeli = string.format(\"%.2f\", math.sqrt((loc.x-feli.x)^2+(loc.y-feli.y)^2+(loc.z-feli.z)^2)/200000)\nlocal distsinnen = string.format(\"%.2f\", math.sqrt((loc.x-sinnen.x)^2+(loc.y-sinnen.y)^2+(loc.z-sinnen.z)^2)/200000)\nlocal disttalemai = string.format(\"%.2f\", math.sqrt((loc.x-talemai.x)^2+(loc.y-talemai.y)^2+(loc.z-talemai.z)^2)/200000)\nlocal distsicari = string.format(\"%.2f\", math.sqrt((loc.x-sicari.x)^2+(loc.y-sicari.y)^2+(loc.z-sicari.z)^2)/200000)\n\n\n\n      for i = 1,1 do\n   local button = {id = (\"b\"..1), enabled=true, td=\"<td>\", top=2/100, bottom=13/100, left=1/100, right=28/100}\n    table.insert(MapScreenButtons, button)\nend\n  for i = 2,2 do\n   local button = {id = (\"b\"..2), enabled=true, td=\"<td>\", top=15/100, bottom=26/100, left=1/100, right=30/100}\n    table.insert(MapScreenButtons, button)\nend\n    for i = 3,3 do\n    local button = {id = (\"b\"..3), enabled=true, td=\"<td>\", top=27/100, bottom=38/100, left=1/100, right=28/100}\n      table.insert(MapScreenButtons, button)\nend\n    for i = 4,4 do   \n    local button = {id = (\"b\"..4), enabled=true, td=\"<td>\", top=39/100, bottom=50/100, left=1/100, right=28/100}\n      table.insert(MapScreenButtons, button)\nend\n    for i = 5,5 do   \n    local button = {id = (\"b\"..5), enabled=true, td=\"<td>\", top=51/100, bottom=62/100, left=1/100, right=28/100}\n      table.insert(MapScreenButtons, button)\nend\n    for i = 6,6 do   \n    local button = {id = (\"b\"..6), enabled=true, td=\"<td>\", top=64/100, bottom=75/100, left=1/100, right=28/100}\n      table.insert(MapScreenButtons, button)\nend\n    for i = 7,7 do   \n    local button = {id = (\"b\"..7), enabled=true, td=\"<td>\", top=2/100, bottom=13/100, left=75/100, right=100/100}\n      table.insert(MapScreenButtons, button)\nend\n    for i = 8,8 do   \n    local button = {id = (\"b\"..8), enabled=true, td=\"<td>\", top=15/100, bottom=26/100, left=75/100, right=100/100}\n      table.insert(MapScreenButtons, button)\nend\n    for i = 9,9 do   \n    local button = {id = (\"b\"..9), enabled=true, td=\"<td>\", top=27/100, bottom=38/100, left=75/100, right=100/100}\n      table.insert(MapScreenButtons, button)\nend\n    for i = 10,10 do   \n    local button = {id = (\"b\"..10), enabled=true, td=\"<td>\", top=39/100, bottom=50/100, left=75/100, right=100/100}\n      table.insert(MapScreenButtons, button)\nend\n    for i = 11,11 do   \n    local button = {id = (\"b\"..11), enabled=true, td=\"<td>\", top=51/100, bottom=62/100, left=75/100, right=100/100}\n      table.insert(MapScreenButtons, button)\nend\n    for i = 12,12 do   \n    local button = {id = (\"b\"..12), enabled=true, td=\"<td>\", top=64/100, bottom=75/100, left=75/100, right=100/100}\n      table.insert(MapScreenButtons, button)\nend\n    for i = 13,13 do   \n    local button = {id = (\"b\"..13), enabled=true, td=\"<td>\", top=90/100, bottom=100/100, left=1/100, right=18/100}\n    table.insert(MapScreenButtons, button)\nend\nfunction evaluateButtons()\n  local selected = 0\n  \n  if #MapScreenButtons >= 1 then\n -- Set button styles\n       for i, button in ipairs(MapScreenButtons) do\n            if button.left < MapScreenMouseX and MapScreenMouseX < button.right and button.top < MapScreenMouseY and MapScreenMouseY < button.bottom then\n                if MapScreenMouseDown and MapScreenButtonSelected == i then\n                end\n                selected = i\n            end\n            if not button.enabled then\n            end\n\n        end\n  end\n  return selected\nend\n\nfunction onButtonDown(buttonNo)\n  local button = MapScreenButtons[buttonNo]    \n  if not button or not button.enabled then\n\treturn\n  end\nend\nfunction onButtonUp(buttonNo)\n  local button = MapScreenButtons[buttonNo]    \n  if not button or not button.enabled then\n    return\n  end\nfunction onClick(buttonNo)\n  local button = MapScreenButtons[buttonNo]    \n  if not button or not button.enabled then\n    return\n  end\nend\n  if buttonNo == 1 then\ndestX = 0\ndestY = 0\nsudistance = distalioth\n  elseif buttonNo == 2 then\ndestX = 43\ndestY = -56\nsudistance = distmadis\n  elseif buttonNo == 3 then\ndestX = 73\ndestY = -27\nsudistance = distthades        \n  elseif buttonNo == 4 then\ndestX = -33\ndestY = -139\nsudistance = disttalemai\n  elseif buttonNo == 5 then\ndestX = -109\ndestY = -56\nsudistance = distfeli        \n  elseif buttonNo == 6 then\ndestX = 131\ndestY = -68\nsudistance = distsicari        \n  elseif buttonNo == 7 then\ndestX = 35\ndestY = 214\nsudistance = distsymeon\n  elseif buttonNo == 8 then\ndestX = 146\ndestY = -74\nsudistance = distsinnen        \n  elseif buttonNo == 9 then\ndestX = -235\ndestY = -32\nsudistance = distjago       \n  elseif buttonNo == 10 then\ndestX = 202\ndestY = -137\nsudistance = distteoma        \n  elseif buttonNo == 11 then\ndestX = 7\ndestY = 247\nsudistance = distion        \n  elseif buttonNo == 12 then\ndestX = 247\ndestY = 34\nsudistance = distlacobus\n  elseif buttonNo == 13 then\n  unit.exit()\n  end\nend\n\nfunction updateScreen() \nwarpmath = math.floor(math.floor(core.getConstructMass()/ 1000)  * sudistance * 0.00025)\nhtml= ([[\n<svg class=\"bootstrap\" viewBox=\"0 0 1024 1024\" style=\"width:100%; height:100%\">      \n<circle cx=\"500\" cy=\"500\" r=\"400\" stroke=\"darkgreen\" stroke-width=\"3\" transform=\"\"></circle><circle cx=\"500\" cy=\"500\" r=\"350\" stroke=\"darkgreen\" stroke-width=\"3\" transform=\"\" stroke-opacity=\"0.2\"></circle><circle cx=\"500\" cy=\"500\" r=\"300\" stroke=\"darkgreen\" stroke-width=\"3\" transform=\"\"></circle><circle cx=\"500\" cy=\"500\" r=\"250\" stroke=\"darkgreen\" stroke-width=\"3\" transform=\"\" stroke-opacity=\"0.2\"></circle><circle cx=\"500\" cy=\"500\" r=\"200\" stroke=\"darkgreen\" stroke-width=\"3\" transform=\"\"></circle><circle cx=\"500\" cy=\"500\" r=\"150\" stroke=\"darkgreen\" stroke-width=\"3\" transform=\"\" stroke-opacity=\"0.2\"></circle><circle cx=\"500\" cy=\"500\" r=\"100\" stroke=\"lightblue\" stroke-width=\"3\" transform=\"\"></circle><circle cx=\"500\" cy=\"500\" r=\"50\" stroke=\"lightblue\" stroke-width=\"3\" transform=\"\" stroke-opacity=\"0.2\"></circle><circle cx=\"500\" cy=\"500\" r=\"20\" stroke=\"Orange\" stroke-width=\"2\" transform=\"\"></circle><text x=\"510\" y=\"510\" fill=\"Yellow\">Helios</text><circle cx=\"-0.00\" cy=\"0\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"-0.00\" y=\"0\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Alioth</text><circle cx=\"7.16\" cy=\"247.59\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"7.16\" y=\"247.59\" transform=\"translate(480,480)\" fill=\"white\" font-size=\"20\">Ion</text><circle cx=\"35.41\" cy=\"214.09\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"35.41\" y=\"214.09\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Symeon</text><circle cx=\"-33.09\" cy=\"-139.41\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"-33.09\" y=\"-139.41\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Talemai</text><circle cx=\"202.16\" cy=\"-136.66\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"202.16\" y=\"-136.66\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Teoma</text><circle cx=\"247.16\" cy=\"33.84\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"247.16\" y=\"33.84\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Lacobus</text><circle cx=\"-108.84\" cy=\"-56.41\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"-108.84\" y=\"-56.41\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Feli</text><circle cx=\"72.91\" cy=\"-27.16\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"72.91\" y=\"-27.16\" transform=\"translate(500,485)\" fill=\"white\" font-size=\"20\">Thades</text><circle cx=\"43.66\" cy=\"-56.66\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"43.66\" y=\"-56.66\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Madis</text><circle cx=\"-235.34\" cy=\"-31.91\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"-235.34\" y=\"-31.91\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Jago</text><circle cx=\"131.91\" cy=\"-67.91\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"131.91\" y=\"-67.91\" transform=\"translate(475,480)\" fill=\"white\" font-size=\"20\">Sicari</text><circle cx=\"146.66\" cy=\"-74.16\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"146.66\" y=\"-74.16\" transform=\"translate(515,480)\" fill=\"white\" font-size=\"20\">Sinnen</text>\n<line stroke-linecap=\"undefined\" stroke-linejoin=\"undefined\" id=\"svg_1\" y2=\"]]..destY..[[\" x2=\"]]..destX..[[\" y1=\"]]..locY..[[\" x1=\"]]..locX..[[\" transform=\"translate(500,500)\" stroke-width=\"5\" stroke=\"#ff0000\" fill=\"none\"/>        \n<circle cx=\"]]..locX..[[\" cy=\"]]..locY..[[\" r=\"3\" stroke=\"black\" stroke-width=\"1\" fill=\"limegreen\" transform=\"translate(500,500)\"></circle>\n<text x=\"]]..locX..[[\" y=\"]]..locY..[[\" transform=\"translate(500,500)\" \nfill=\"limegreen\" font-size= \"4.5vh\" font-weight= \"bold\">//SHIP POSITION</text>\n</svg>\n<svg class=\"bootstrap\" viewBox=\"0 0 1024 612\" style=\"width:100%; height:100%\">\n <g>\n  <title>Layer 1</title>\n  <g id=\"svg_24\">\n   <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_8\" y=\"70\" x=\"55\" stroke-width=\"0\" fill=\"Yellow\">Alioth  :]]..distalioth..[[ SU</text>\n   <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_14\" y=\"170\" x=\"55\" stroke-width=\"0\" fill=\"Yellow\">Madis  :]]..distmadis..[[ SU</text>\n   <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_17\" y=\"270\" x=\"55\" stroke-width=\"0\" fill=\"Yellow\">Thades  :]]..distthades..[[ SU</text>\n   <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_20\" y=\"370\" x=\"55\" stroke-width=\"0\" fill=\"Yellow\">Talemai  :]]..disttalemai..[[ SU</text>\n   <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_23\" y=\"470\" x=\"55\" stroke-width=\"0\" fill=\"Yellow\">Feli  :]]..distfeli..[[ SU</text>\n   <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_26\" y=\"570\" x=\"55\" stroke-width=\"0\" fill=\"Yellow\">Sicari  :]]..distsicari..[[ SU</text>\n   <g id=\"svg_12\">\n    <rect rx=\"10\" id=\"svg_1\" height=\"50\" width=\"250\" y=\"30\" x=\"15\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n    <rect rx=\"10\" id=\"svg_3\" height=\"50\" width=\"250\" y=\"105\" x=\"15\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n    <rect rx=\"10\" id=\"svg_7\" height=\"50\" width=\"250\" y=\"180\" x=\"15\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n    <rect rx=\"10\" id=\"svg_9\" height=\"50\" width=\"250\" y=\"255\" x=\"15\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n    <rect rx=\"10\" id=\"svg_10\" height=\"50\" width=\"250\" y=\"330\" x=\"15\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n    <rect rx=\"10\" id=\"svg_11\" height=\"50\" width=\"250\" y=\"405\" x=\"15\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n   </g>\n  </g>\n  <g id=\"svg_40\">\n   <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_25\" y=\"70\" x=\"997.163642\" stroke-width=\"0\" fill=\"Yellow\">Symeon  :]]..distsymeon..[[ SU</text>\n   <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_27\" y=\"170\" x=\"997.163642\" stroke-width=\"0\" fill=\"Yellow\">Sinnen  :]]..distsinnen..[[ SU</text>\n   <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_28\" y=\"270\" x=\"997.163642\" stroke-width=\"0\" fill=\"Yellow\">Jago  :]]..distjago..[[ SU</text>\n   <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_30\" y=\"370\" x=\"997.163642\" stroke-width=\"0\" fill=\"Yellow\">Teoma  :]]..distteoma..[[ SU</text>\n   <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_31\" y=\"470\" x=\"997.163642\" stroke-width=\"0\" fill=\"Yellow\">Ion  :]]..distion..[[ SU</text>\n   <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_32\" y=\"570\" x=\"997.163642\" stroke-width=\"0\" fill=\"Yellow\">Lacobus  :]]..distlacobus..[[ SU</text>\n   <g id=\"svg_39\">\n    <rect rx=\"10\" id=\"svg_33\" height=\"50\" width=\"250\" y=\"30\" x=\"760\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n    <rect rx=\"10\" id=\"svg_34\" height=\"50\" width=\"250\" y=\"105\" x=\"760\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n    <rect rx=\"10\" id=\"svg_35\" height=\"50\" width=\"250\" y=\"180\" x=\"760\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n    <rect rx=\"10\" id=\"svg_36\" height=\"50\" width=\"250\" y=\"255\" x=\"760\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n    <rect rx=\"10\" id=\"svg_37\" height=\"50\" width=\"250\" y=\"330\" x=\"760\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n    <rect rx=\"10\" id=\"svg_38\" height=\"50\" width=\"250\" y=\"405\" x=\"760\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n   </g>\n  </g>\n </g>\n<text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"30\" id=\"svg_32\" y=\"700\" x=\"20\" stroke-width=\"0\" fill=\"LightBlue\">Est. Warp Cost: ]]..warpmath..[[</text>\n  <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"30\" id=\"svg_32\" y=\"750\" x=\"20\" stroke-width=\"0\" fill=\"LightBlue\">Construct Weight: ]]..math.floor(core.getConstructMass()/ 1000)..[[ tons</text>\n    \n</svg> \n    ]])\nscreen.setHTML(html)\nend\nunit.setTimer(\"spacemap\",.08)\n\n","filter":{"args":[],"signature":"start()","slotKey":"-1"},"key":"2"},{"code":"updateScreen()","filter":{"args":[{"value":"spacemap"}],"signature":"tick(timerId)","slotKey":"-1"},"key":"3"}],"methods":[],"events":[]}


---------- JayleBreak Atlas and Functions Version---------

function Atlas()
        return {
    [0] = {
      [1]={
        GM=6930729684,
        bodyId=1,
        center={x=17465536.000,y=22665536.000,z=-34464.000},
        name='Madis',
        planetarySystemId=0,
        radius=44300
      },
      [2]={
        GM=157470826617,
        bodyId=2,
        center={x=-8.000,y=-8.000,z=-126303.000},
        name='Alioth',
        planetarySystemId=0,
        radius=126068
      },
      [3]={
        GM=11776905000,
        bodyId=3,
        center={x=29165536.000,y=10865536.000,z=65536.000},
        name='Thades',
        planetarySystemId=0,
        radius=49000
      },
      [4]={
        GM=14893847582,
        bodyId=4,
        center={x=-13234464.000,y=55765536.000,z=465536.000},
        name='Talemai',
        planetarySystemId=0,
        radius=57450
      },
      [5]={
        GM=16951680000,
        bodyId=5,
        center={x=-43534464.000,y=22565536.000,z=-48934464.000},
        name='Feli',
        planetarySystemId=0,
        radius=60000
      },
      [6]={
        GM=10502547741,
        bodyId=6,
        center={x=52765536.000,y=27165538.000,z=52065535.000},
        name='Sicari',
        planetarySystemId=0,
        radius=51100
      },
      [7]={
        GM=13033380591,
        bodyId=7,
        center={x=58665538.000,y=29665535.000,z=58165535.000},
        name='Sinnen',
        planetarySystemId=0,
        radius=54950
      },
      [8]={
        GM=18477723600,
        bodyId=8,
        center={x=80865538.000,y=54665536.000,z=-934463.940},
        name='Teoma',
        planetarySystemId=0,
        radius=62000
      },
      [9]={
        GM=18606274330,
        bodyId=9,
        center={x=-94134462.000,y=12765534.000,z=-3634464.000},
        name='Jago',
        planetarySystemId=0,
        radius=61590
      },
      [10]={
        GM=78480000,
        bodyId=10,
        center={x=17448118.224,y=22966846.286,z=143078.820},
        name='Madis Moon 1',
        planetarySystemId=0,
        radius=10000
      },
      [11]={
        GM=237402000,
        bodyId=11,
        center={x=17194626.000,y=22243633.880,z=-214962.810},
        name='Madis Moon 2',
        planetarySystemId=0,
        radius=11000
      },
      [12]={
        GM=265046609,
        bodyId=12,
        center={x=17520614.000,y=22184730.000,z=-309989.990},
        name='Madis Moon 3',
        planetarySystemId=0,
        radius=15005
      },
      [21]={
        GM=2118960000,
        bodyId=21,
        center={x=457933.000,y=-1509011.000,z=115524.000},
        name='Alioth Moon 1',
        planetarySystemId=0,
        radius=30000
      },
      [22]={
        GM=2165833514,
        bodyId=22,
        center={x=-1692694.000,y=729681.000,z=-411464.000},
        name='Alioth Moon 4',
        planetarySystemId=0,
        radius=30330
      },
      [26]={
        GM=68234043600,
        bodyId=26,
        center={x=-1404835.000,y=562655.000,z=-285074.000},
        name='Sanctuary',
        planetarySystemId=0,
        radius=83400
      },
      [30]={
        GM=211564034,
        bodyId=30,
        center={x=29214402.000,y=10907080.695,z=433858.200},
        name='Thades Moon 1',
        planetarySystemId=0,
        radius=14002
      },
      [31]={
        GM=264870000,
        bodyId=31,
        center={x=29404193.000,y=10432768.000,z=19554.131},
        name='Thades Moon 2',
        planetarySystemId=0,
        radius=15000
      },
      [40]={
        GM=141264000,
        bodyId=40,
        center={x=-13503090.000,y=55594325.000,z=769838.640},
        name='Talemai Moon 2',
        planetarySystemId=0,
        radius=12000
      },
      [41]={
        GM=106830900,
        bodyId=41,
        center={x=-12800515.000,y=55700259.000,z=325207.840},
        name='Talemai Moon 3',
        planetarySystemId=0,
        radius=11000
      },
      [42]={
        GM=264870000,
        bodyId=42,
        center={x=-13058408.000,y=55781856.000,z=740177.760},
        name='Talemai Moon 1',
        planetarySystemId=0,
        radius=15000
      },
      [50]={
        GM=499917600,
        bodyId=50,
        center={x=-43902841.780,y=22261034.700,z=-48862386.000},
        name='Feli Moon 1',
        planetarySystemId=0,
        radius=14000
      },
      [70]={
        GM=396912600,
        bodyId=70,
        center={x=58969616.000,y=29797945.000,z=57969449.000},
        name='Sinnen Moon 1',
        planetarySystemId=0,
        radius=17000
      },
      [100]={
        GM=13975172474,
        bodyId=100,
        center={x=98865536.000,y=-13534464.000,z=-934461.990},
        name='Lacobus',
        planetarySystemId=0,
        radius=55650
      },
      [101]={
        GM=264870000,
        bodyId=101,
        center={x=98905288.170,y=-13950921.100,z=-647589.530},
        name='Lacobus Moon 3',
        planetarySystemId=0,
        radius=15000
      },
      [102]={
        GM=444981600,
        bodyId=102,
        center={x=99180968.000,y=-13783862.000,z=-926156.400},
        name='Lacobus Moon 1',
        planetarySystemId=0,
        radius=18000
      },
      [103]={
        GM=211503600,
        bodyId=103,
        center={x=99250052.000,y=-13629215.000,z=-1059341.400},
        name='Lacobus Moon 2',
        planetarySystemId=0,
        radius=14000
      },
      [110]={
        GM=9204742375,
        bodyId=110,
        center={x=14165536.000,y=-85634465.000,z=-934464.300},
        name='Symeon',
        planetarySystemId=0,
        radius=49050
      },
      [120]={
        GM=7135606629,
        bodyId=120,
        center={x=2865536.700,y=-99034464.000,z=-934462.020},
        name='Ion',
        planetarySystemId=0,
        radius=44950
      },
      [121]={
        GM=106830900,
        bodyId=121,
        center={x=2472916.800,y=-99133747.000,z=-1133582.800},
        name='Ion Moon 1',
        planetarySystemId=0,
        radius=11000
      },
      [122]={
        GM=176580000,
        bodyId=122,
        center={x=2995424.500,y=-99275010.000,z=-1378480.700},
        name='Ion Moon 2',
        planetarySystemId=0,
        radius=15000
      }  
     }
    }
    end
function PlanetRef() 
--[[ 
  Provide coordinate transforms and access to kinematic related parameters
  Author: JayleBreak
  Usage (unit.start):
  PlanetaryReference = require('planetref')
  galaxyReference = PlanetaryReference(referenceTableSource)
  helios = galaxyReference[0] -- PlanetaryReference.PlanetarySystem instance
  alioth = helios[2]          -- PlanetaryReference.BodyParameters instance
  Methods:
    PlanetaryReference:getPlanetarySystem - based on planetary system ID.
    PlanetaryReference.isMapPosition - 'true' if an instance of 'MapPosition'
    PlanetaryReference.createBodyParameters - for entry into reference table
    PlanetaryReference.BodyParameters - a class containing a body's information.
    PlanetaryReference.MapPosition - a class for map coordinates
    PlanetaryReference.PlanetarySystem - a container for planetary system info.
    PlanetarySystem:castIntersections - from a position in a given direction.
    PlanetarySystem:closestBody - to the specified coordinates.
    PlanetarySystem:convertToBodyIdAndWorldCoordinates - from map coordinates.
    PlanetarySystem:getBodyParameters - from reference table.
    PlanetarySystem:getPlanetarySystemId - for the instance.
    BodyParameters:convertToWorldCoordinates - from map coordinates
    BodyParameters:convertToMapPosition - from world coordinates
    BodyParameters:getAltitude - of world coordinates
    BodyParameters:getDistance - from center to world coordinates
    BodyParameters:getGravity - at a given position in world coordinates.
  Description
  An instance of the 'PlanetaryReference' "class" can contain transform and
  kinematic reference information for all planetary systems in DualUniverse.
  Each planetary system is identified by a numeric identifier. Currently,
  the only planetary system, Helios, has the identifier: zero. This "class"
  supports the indexing ('[]') operation which is equivalent to the
  use of the 'getPlanetarySystem' method. It also supports the 'pairs()'
  method for iterating over planetary systems.

  An instance of the 'PlanetarySystem' "class" contains all reference
  information for a specific system. It supports the indexing ('[]') and
  'pairs()' functions which allows iteration over each "body" in the
  system where the key is the numeric body ID. It also supports the
  'tostring()' method.
  An instance of the 'BodyParameters' "class" contains all reference
  information for a single celestial "body" (a moon or planet). It supports
  the 'tostring()' method, and contains the data members:
          planetarySystemId - numeric planetary system ID
          bodyId            - numeric body ID
          radius            - radius of the body in meters (zero altitude)
          center            - world coordinates of the body's center position
          GM                - the gravitation parameter (g = GM/radius^2)
  Note that the user is allowed to add custom fields (e.g. body name), but
  should insure that complex table values have the '__tostring' metamethod
  implemented.
  Transform and Kinematics:
  "World" coordinates is a cartesian coordinate system with an origin at an
  arbitrary fixed point in a planetary system and with distances measured in
  meters. The coordinates are expressible either as a simple table of 3 values
  or an instance of the 'vec3' class.  In either case, the planetary system
  identity is implicit.
  "Map" coordinates is a geographic coordinate system with an origin at the
  center of an identified (by a numeric value) celestial body which is a
  member of an identified (also a numeric value) planetary system. Note that
  the convention that latitude, longitude, and altitude values will be the
  position's x, y, and z world coordinates in the special case of body ID 0.
  The kinematic parameters in the reference data permit calculations of the
  gravitational attraction of the celestial body on other objects.
  Reference Data:
  This is an example of reference data with a single entry assigned to
  planetary system ID 0, and body ID 2 ('Alioth'):
    referenceTable = {
          [0] = { [2] = { planetarySystemId = 0,
                          bodyId = 2,
                          radius = 126068,
                          center = vec3({x=-8, y=-8, z=-126303}),
                          GM = 1.572199+11 } -- as in F=-GMm/r^2
          }
      }
    ref=PlanetaryReference(referenceTable)
  Collecting Reference Data:
  A combination of information from the "Map" screen in the DU user interface,
  and values reported by the DU Lua API can be the source of the reference
  table's data (planetarySystemId, bodyId, and surfaceArea is from the user
  interface):
    referenceTable = {}
    referenceTable[planetarySystemId][bodyId] =
         PlanetaryReference.createBodyParameters(planetarySystemId,
                                                 bodyId,
                                                 surfaceArea,
                                                 core.getConstructWorldPos(),
                                                 core.getWorldVertical(),
                                                 core.getAltitude(),
                                                 core.g())
  Adapting Data Sources:
  Other sources of data can be adapted or converted. An example of adapting a
  table, defined in the file: 'planets.lua', containing information on a single
  planetary system and using celestial body name as the key follows (note that
  a 'name' field is added to the BodyParameters instance transparently after
  construction, and the '__pairs' meta function is required to support the
  'closestBody' and '__tostring' methods):
    ref=PlanetaryReference(
        {[0] = setmetatable(require('planets'),
                        { __index = function(bodies, bodyId)
                             for _,v in pairs(bodies) do
                                 if v and v.bodyId == bodyId then return v end
                             end
                             return nil
                           end,
                         __pairs = function(bodies)
                             return function(t, k)
                                     local nk, nv = next(t, k)
                                     if nv then
                                         local GM = nv.gravity * nv.radius^2
                                         local bp = BodyParameters(0,
                                                                   nv.id,
                                                                   nv.radius,
                                                                   nv.pos,
                                                                   GM)
                                         bp.name = nk
                                         return nk, bp
                                    end
                                    return nk, nv
                                 end, bodies, nil
                           end })
    })

  Converting Data Sources:
  An instance of 'PlanetaryReference' that has been adapted to a data source
  can be used to convert that source to simple table. For example,
  using the adapted instance shown above:
    load('convertedData=' .. tostring(ref))()
    newRef=PlanetaryReference(convertedData)
  Also See: kepler.lua
  ]]--
--[[                    START OF LOCAL IMPLEMENTATION DETAILS             ]]--
-- Type checks
local function isNumber(n)  return type(n)           == 'number' end
local function isSNumber(n) return type(tonumber(n)) == 'number' end
local function isTable(t)   return type(t)           == 'table'  end
local function isString(s)  return type(s)           == 'string' end
local function isVector(v)  return isTable(v)
                                    and isNumber(v.x and v.y and v.z) end
local function isMapPosition(m) return isTable(m) and isNumber(m.latitude  and
                                                               m.longitude and
                                                               m.altitude  and
                                                               m.bodyId    and
                                                               m.systemId) end
-- Constants
local deg2rad    = math.pi/180
local rad2deg    = 180/math.pi
local epsilon    = 1e-10
local num        = ' *([+-]?%d+%.?%d*e?[+-]?%d*)'
local posPattern = '::pos{' .. num .. ',' .. num .. ',' ..  num .. ',' ..
                   num ..  ',' .. num .. '}'
-- Utilities
local utils  = require('cpml.utils')
local vec3   = require('cpml.vec3')
local clamp  = utils.clamp
local function float_eq(a,b)
    if a == 0 then return math.abs(b) < 1e-09 end
    if b == 0 then return math.abs(a) < 1e-09 end
    return math.abs(a - b) < math.max(math.abs(a),math.abs(b))*epsilon
end
local function formatNumber(n)
    local result = string.gsub(
                    string.reverse(string.format('%.4f',n)),
                    '^0*%.?','')
    return result == '' and '0' or string.reverse(result)
end
local function formatValue(obj)
    if isVector(obj) then
        return string.format('{x=%.3f,y=%.3f,z=%.3f}', obj.x, obj.y, obj.z)
    end
    if isTable(obj) and not getmetatable(obj) then
        local list = {}
        local nxt  = next(obj)
        if type(nxt) == 'nil' or nxt == 1 then -- assume this is an array
            list = obj
        else
            for k,v in pairs(obj) do
                local value = formatValue(v)
                if type(k) == 'number' then
                    table.insert(list, string.format('[%s]=%s', k, value))
                else
                    table.insert(list, string.format('%s=%s',   k, value))
                end
            end
        end
        return string.format('{%s}', table.concat(list, ','))
    end
    if isString(obj) then
        return string.format("'%s'", obj:gsub("'",[[\']]))
    end
    return tostring(obj)
end
-- CLASSES
-- BodyParameters: Attributes of planetary bodies (planets and moons)
local BodyParameters = {}
BodyParameters.__index = BodyParameters
BodyParameters.__tostring =
    function(obj, indent)
        local sep = indent or ''
        local keys = {}
        for k in pairs(obj) do table.insert(keys, k) end
        table.sort(keys)
        local list = {}
        for _, k in ipairs(keys) do
            local value = formatValue(obj[k])
            if type(k) == 'number' then
                table.insert(list, string.format('[%s]=%s', k, value))
            else
                table.insert(list, string.format('%s=%s', k, value))
            end
        end
        if indent then
            return string.format('%s%s',
                                 indent,
                                 table.concat(list, ',\n' .. indent))
        end
        return string.format('{%s}', table.concat(list, ','))
    end
BodyParameters.__eq = function(lhs, rhs)
        return lhs.planetarySystemId == rhs.planetarySystemId and
               lhs.bodyId            == rhs.bodyId            and
               float_eq(lhs.radius, rhs.radius)               and
               float_eq(lhs.center.x, rhs.center.x)           and
               float_eq(lhs.center.y, rhs.center.y)           and
               float_eq(lhs.center.z, rhs.center.z)           and
               float_eq(lhs.GM, rhs.GM)
    end
local function mkBodyParameters(systemId, bodyId, radius, worldCoordinates, GM)
    -- 'worldCoordinates' can be either table or vec3
    assert(isSNumber(systemId),
           'Argument 1 (planetarySystemId) must be a number:' .. type(systemId))
    assert(isSNumber(bodyId),
           'Argument 2 (bodyId) must be a number:' .. type(bodyId))
    assert(isSNumber(radius),
           'Argument 3 (radius) must be a number:' .. type(radius))
    assert(isTable(worldCoordinates),
           'Argument 4 (worldCoordinates) must be a array or vec3.' ..
           type(worldCoordinates))
    assert(isSNumber(GM),
           'Argument 5 (GM) must be a number:' .. type(GM))
    return setmetatable({planetarySystemId = tonumber(systemId),
                         bodyId            = tonumber(bodyId),
                         radius            = tonumber(radius),
                         center            = vec3(worldCoordinates),
                         GM                = tonumber(GM) }, BodyParameters)
end
-- MapPosition: Geographical coordinates of a point on a planetary body.
local MapPosition = {}
MapPosition.__index = MapPosition
MapPosition.__tostring = function(p)
        return string.format('::pos{%d,%d,%s,%s,%s}',
                             p.systemId,
                             p.bodyId,
                             formatNumber(p.latitude*rad2deg),
                             formatNumber(p.longitude*rad2deg),
                             formatNumber(p.altitude))
    end
MapPosition.__eq       = function(lhs, rhs)
        return lhs.bodyId   == rhs.bodyId              and
               lhs.systemId == rhs.systemId            and
               float_eq(lhs.latitude,   rhs.latitude)  and
               float_eq(lhs.altitude,   rhs.altitude)  and
               (float_eq(lhs.longitude, rhs.longitude) or
                float_eq(lhs.latitude, math.pi/2)      or
                float_eq(lhs.latitude, -math.pi/2))
    end
-- latitude and longitude are in degrees while altitude is in meters
local function mkMapPosition(overload, bodyId, latitude, longitude, altitude)
    local systemId = overload -- Id or '::pos{...}' string
    if isString(overload) and not longitude and not altitude and
                              not bodyId    and not latitude then
        systemId, bodyId, latitude, longitude, altitude =
                                            string.match(overload, posPattern)
        assert(systemId, 'Argument 1 (position string) is malformed.')
    else
        assert(isSNumber(systemId),
               'Argument 1 (systemId) must be a number:' .. type(systemId))
        assert(isSNumber(bodyId),
               'Argument 2 (bodyId) must be a number:' .. type(bodyId))
        assert(isSNumber(latitude),
               'Argument 3 (latitude) must be in degrees:' .. type(latitude))
        assert(isSNumber(longitude),
               'Argument 4 (longitude) must be in degrees:' .. type(longitude))
        assert(isSNumber(altitude),
               'Argument 5 (altitude) must be in meters:' .. type(altitude))
    end
    systemId  = tonumber(systemId)
    bodyId    = tonumber(bodyId)
    latitude  = tonumber(latitude)
    longitude = tonumber(longitude)
    altitude  = tonumber(altitude)
    if bodyId == 0 then -- this is a hack to represent points in space
        return setmetatable({latitude  = latitude,
                             longitude = longitude,
                             altitude  = altitude,
                             bodyId    = bodyId,
                             systemId  = systemId}, MapPosition)
    end
    return setmetatable({latitude  = deg2rad*clamp(latitude, -90, 90),
                         longitude = deg2rad*(longitude % 360),
                         altitude  = altitude,
                         bodyId    = bodyId,
                         systemId  = systemId}, MapPosition)
end
-- PlanetarySystem - map body IDs to BodyParameters
local PlanetarySystem = {}
PlanetarySystem.__index = PlanetarySystem
PlanetarySystem.__tostring =
    function (obj, indent)
        local sep = indent and (indent .. '  ' )
        local bdylist = {}
        local keys = {}
        for k in pairs(obj) do table.insert(keys, k) end
        table.sort(keys)
        for _, bi in ipairs(keys) do
            bdy = obj[bi]
            local bdys = BodyParameters.__tostring(bdy, sep)
            if indent then
                table.insert(bdylist,
                             string.format('[%s]={\n%s\n%s}',
                                           bi, bdys, indent))
            else
                table.insert(bdylist, string.format('  [%s]=%s', bi, bdys))
            end
        end
        if indent then
            return string.format('\n%s%s%s',
                                 indent,
                                 table.concat(bdylist, ',\n' .. indent),
                                 indent)
        end
        return string.format('{\n%s\n}', table.concat(bdylist, ',\n'))
    end
local function mkPlanetarySystem(referenceTable)
    local atlas = {}
    local pid
    for _, v in pairs(referenceTable) do
        local id = v.planetarySystemId
        if type(id) ~= 'number' then
            error('Invalid planetary system ID: ' .. tostring(id))
        elseif pid and id ~= pid then
            error('Mismatch planetary system IDs: ' .. id .. ' and '
                  .. pid)
        end
        local bid = v.bodyId
        if type(bid) ~= 'number' then
            error('Invalid body ID: ' .. tostring(bid))
        elseif atlas[bid] then
            error('Duplicate body ID: ' .. tostring(bid))
        end
        setmetatable(v.center, getmetatable(vec3.unit_x))
        atlas[bid] = setmetatable(v, BodyParameters)
        pid = id
    end
    return setmetatable(atlas, PlanetarySystem)
end
-- PlanetaryReference - map planetary system ID to PlanetarySystem
PlanetaryReference = {}
local function mkPlanetaryReference(referenceTable)
    return setmetatable({ galaxyAtlas = referenceTable or {} },
                          PlanetaryReference)
end
PlanetaryReference.__index        = 
    function(t,i)
        if type(i) == 'number' then
            local system = t.galaxyAtlas[i]
            return mkPlanetarySystem(system)
        end
        return rawget(PlanetaryReference, i)
    end
PlanetaryReference.__pairs        =
    function(obj)
        return  function(t, k)
                    local nk, nv = next(t, k)
                    return nk, nv and mkPlanetarySystem(nv)
                end, obj.galaxyAtlas, nil
    end
PlanetaryReference.__tostring     =
    function (obj)
        local pslist = {}
        for _,ps in pairs(obj or {}) do
            local psi = ps:getPlanetarySystemId()
            local pss = PlanetarySystem.__tostring(ps, '    ')
            table.insert(pslist,
                         string.format('  [%s]={%s\n  }', psi, pss))
        end
        return string.format('{\n%s\n}\n', table.concat(pslist,',\n'))
    end
--[[                       START OF PUBLIC INTERFACE                       ]]--
-- PlanetaryReference CLASS METHODS:
--
-- BodyParameters - create an instance of BodyParameters class
-- planetarySystemId  [in]: the body's planetary system ID.
-- bodyId             [in]: the body's ID.
-- radius             [in]: the radius in meters of the planetary body.
-- bodyCenter         [in]: the world coordinates of the center (vec3 or table).
-- GM                 [in]: the body's standard gravitational parameter.
-- return: an instance of BodyParameters class.
--
PlanetaryReference.BodyParameters = mkBodyParameters
--
-- MapPosition - create an instance of the MapPosition class
-- overload [in]: either a planetary system ID or a position string ('::pos...')
-- bodyId [in]:   (ignored if overload is a position string) the body's ID.
-- latitude [in]: (ignored if overload is a position string) the latitude.
-- longitude [in]:(ignored if overload is a position string) the longitude.
-- altitude [in]: (ignored if overload is a position string) the altitude.
-- return: the class instance
--
PlanetaryReference.MapPosition    = mkMapPosition
--
-- PlanetarySystem - create an instance of PlanetarySystem class
-- referenceData [in]: a table (indexed by bodyId) of body reference info.
-- return: the class instance
--
PlanetaryReference.PlanetarySystem = mkPlanetarySystem
--
-- createBodyParameters - create an instance of BodyParameters class
-- planetarySystemId  [in]: the body's planetary system ID.
-- bodyId             [in]: the body's ID.
-- surfaceArea        [in]: the body's surface area in square meters.
-- aPosition          [in]: world coordinates of a position near the body.
-- verticalAtPosition [in]: a vector pointing towards the body center.
-- altitudeAtPosition [in]: the altitude in meters at the position.
-- gravityAtPosition  [in]: the magnitude of the gravitational acceleration.
-- return: an instance of BodyParameters class.
--
function PlanetaryReference.createBodyParameters(planetarySystemId,
                                                 bodyId,
                                                 surfaceArea,
                                                 aPosition,
                                                 verticalAtPosition,
                                                 altitudeAtPosition,
                                                 gravityAtPosition)
    assert(isSNumber(planetarySystemId),
           'Argument 1 (planetarySystemId) must be a number:' ..
           type(planetarySystemId))
    assert(isSNumber(bodyId),
           'Argument 2 (bodyId) must be a number:' .. type(bodyId))
    assert(isSNumber(surfaceArea),
           'Argument 3 (surfaceArea) must be a number:' .. type(surfaceArea))
    assert(isTable(aPosition),
           'Argument 4 (aPosition) must be an array or vec3:' ..
           type(aPosition))
    assert(isTable(verticalAtPosition),
           'Argument 5 (verticalAtPosition) must be an array or vec3:' ..
           type(verticalAtPosition))
    assert(isSNumber(altitudeAtPosition),
           'Argument 6 (altitude) must be in meters:' ..
           type(altitudeAtPosition))
    assert(isSNumber(gravityAtPosition),
           'Argument 7 (gravityAtPosition) must be number:' ..
           type(gravityAtPosition))
    local radius   = math.sqrt(surfaceArea/4/math.pi)
    local distance = radius + altitudeAtPosition
    local center   = vec3(aPosition) + distance*vec3(verticalAtPosition)
    local GM       = gravityAtPosition * distance * distance
    return mkBodyParameters(planetarySystemId, bodyId, radius, center, GM)
end
--
-- isMapPosition - check for the presence of the 'MapPosition' fields
-- valueToTest [in]: the value to be checked
-- return: 'true' if all required fields are present in the input value
--
PlanetaryReference.isMapPosition  = isMapPosition
-- PlanetaryReference INSTANCE METHODS:
--
-- getPlanetarySystem - get the planetary system using ID or MapPosition as key
-- overload [in]: either the planetary system ID or a MapPosition that has it.
-- return: instance of 'PlanetarySystem' class or nil on error
--
function PlanetaryReference:getPlanetarySystem(overload)
    --if galaxyAtlas then
        local planetarySystemId = overload
        if isMapPosition(overload) then
            planetarySystemId = overload.systemId
        end
        if type(planetarySystemId) == 'number' then
            local system = self.galaxyAtlas[i]
            if system then
                if getmetatable(nv) ~= PlanetarySystem then
                    system = mkPlanetarySystem(system)
                end
                return system
            end
        end
    --end
    --return nil
end
-- PlanetarySystem INSTANCE METHODS:
--
-- castIntersections - Find the closest body that intersects a "ray cast".
-- origin [in]: the origin of the "ray cast" in world coordinates
-- direction [in]: the direction of the "ray cast" as a 'vec3' instance.
-- sizeCalculator [in]: (default: returns 1.05*radius) Returns size given body.
-- bodyIds[in]: (default: all IDs in system) check only the given IDs.
-- return: The closest body that blocks the cast or 'nil' if none.
--
function PlanetarySystem:castIntersections(origin,
                                           direction,
                                           sizeCalculator,
                                           bodyIds)
    local sizeCalculator = sizeCalculator or 
                            function (body) return 1.05*body.radius end
    local candidates = {}
    if bodyIds then
        for _,i in ipairs(bodyIds) do candidates[i] = self[i] end
    else
        bodyIds = {}
        for k,body in pairs(self) do
            table.insert(bodyIds, k)
            candidates[k] = body
        end
    end
    local function compare(b1,b2)
        local v1 = candidates[b1].center - origin
        local v2 = candidates[b2].center - origin
        return v1:len() < v2:len()
    end
    table.sort(bodyIds, compare)
    local dir = direction:normalize()
    for i, id in ipairs(bodyIds) do
        local body   = candidates[id]
        local c_oV3  = body.center - origin
        local radius = sizeCalculator(body)
        local dot    = c_oV3:dot(dir)
        local desc   = dot^2 - (c_oV3:len2() - radius^2)
        if desc >= 0 then
            local root     = math.sqrt(desc)
            local farSide  = dot + root
            local nearSide = dot - root
            if nearSide > 0 then
                return body, farSide, nearSide
            elseif farSide > 0 then
                return body, farSide, nil
            end
        end
    end
    return nil, nil, nil
end
--
-- closestBody - find the closest body to a given set of world coordinates
-- coordinates       [in]: the world coordinates of position in space
-- return: an instance of the BodyParameters object closest to 'coordinates'
--
function PlanetarySystem:closestBody(coordinates)
    assert(type(coordinates) == 'table', 'Invalid coordinates.')
    local minDistance2, body
    local coord = vec3(coordinates)
    for _,params in pairs(self) do
        local distance2 = (params.center - coord):len2()
        if not body or distance2 < minDistance2 then
            body         = params
            minDistance2 = distance2
        end
    end
    return body
end
--
-- convertToBodyIdAndWorldCoordinates - map to body Id and world coordinates
-- overload [in]: an instance of MapPosition or a position string ('::pos...)
-- return: a vec3 instance containing the world coordinates or 'nil' on error.
--
function PlanetarySystem:convertToBodyIdAndWorldCoordinates(overload)
    local mapPosition = overload
    if isString(overload) then
        mapPosition = mkMapPosition(overload)
    end
    if mapPosition.bodyId == 0 then
        return 0, vec3(mapPosition.latitude,
                       mapPosition.longitude,
                       mapPosition.altitude)
    end
    local params = self:getBodyParameters(mapPosition)
    if params then
        return mapPosition.bodyId,
               params:convertToWorldCoordinates(mapPosition)
    end
end
--
-- getBodyParameters - get or create an instance of BodyParameters class
-- overload [in]: either an instance of MapPosition or a body's ID.
-- return: a BodyParameters instance or 'nil' if body ID is not found.
--
function PlanetarySystem:getBodyParameters(overload)
    local bodyId = overload
    if isMapPosition(overload) then
        bodyId = overload.bodyId
    end
    assert(isSNumber(bodyId),
               'Argument 1 (bodyId) must be a number:' .. type(bodyId))
    return self[bodyId]
end
--
-- getPlanetarySystemId - get the planetary system ID for this instance
-- return: the planetary system ID or nil if no planets are in the system.
--
function PlanetarySystem:getPlanetarySystemId()
    local k, v = next(self)
    return v and v.planetarySystemId
end
-- BodyParameters INSTANCE METHODS:
--
-- convertToMapPosition - create an instance of MapPosition from coordinates
-- worldCoordinates [in]: the world coordinates of the map position.
-- return: an instance of MapPosition class
--
function BodyParameters:convertToMapPosition(worldCoordinates)
    assert(isTable(worldCoordinates),
           'Argument 1 (worldCoordinates) must be an array or vec3:' ..
           type(worldCoordinates))
    local worldVec  = vec3(worldCoordinates) 
    if self.bodyId == 0 then
        return setmetatable({latitude  = worldVec.x,
                             longitude = worldVec.y,
                             altitude  = worldVec.z,
                             bodyId    = 0,
                             systemId  = self.planetarySystemId}, MapPosition)
    end
    local coords    = worldVec - self.center
    local distance  = coords:len()
    local altitude  = distance - self.radius
    local latitude  = 0
    local longitude = 0
    if not float_eq(distance, 0) then
        local phi = math.atan(coords.y, coords.x)
        longitude = phi >= 0 and phi or (2*math.pi + phi)
        latitude  = math.pi/2 - math.acos(coords.z/distance)
    end
    return setmetatable({latitude  = latitude,
                         longitude = longitude,
                         altitude  = altitude,
                         bodyId    = self.bodyId,
                         systemId  = self.planetarySystemId}, MapPosition)
end
--
-- convertToWorldCoordinates - convert a map position to world coordinates
-- overload [in]: an instance of MapPosition or a position string ('::pos...')
--
function BodyParameters:convertToWorldCoordinates(overload)
    local mapPosition = isString(overload) and
                                           mkMapPosition(overload) or overload
    if mapPosition.bodyId == 0 then -- support deep space map position
        return vec3(mapPosition.latitude,
                    mapPosition.longitude,
                    mapPosition.altitude)
    end
    assert(isMapPosition(mapPosition),
           'Argument 1 (mapPosition) is not an instance of "MapPosition".')
    assert(mapPosition.systemId == self.planetarySystemId,
           'Argument 1 (mapPosition) has a different planetary system ID.')
    assert(mapPosition.bodyId == self.bodyId,
           'Argument 1 (mapPosition) has a different planetary body ID.')
    local xproj = math.cos(mapPosition.latitude)
    return self.center + (self.radius + mapPosition.altitude) *
           vec3(xproj*math.cos(mapPosition.longitude),
                xproj*math.sin(mapPosition.longitude),
                math.sin(mapPosition.latitude))
end
--
-- getAltitude - calculate the altitude of a point given in world coordinates.
-- worldCoordinates [in]: the world coordinates of the point.
-- return: the altitude in meters
--
function BodyParameters:getAltitude(worldCoordinates)
    return (vec3(worldCoordinates) - self.center):len() - self.radius
end
--
-- getDistance - calculate the distance to a point given in world coordinates.
-- worldCoordinates [in]: the world coordinates of the point.
-- return: the distance in meters
--
function BodyParameters:getDistance(worldCoordinates)
    return (vec3(worldCoordinates) - self.center):len()
end
--
-- getGravity - calculate the gravity vector induced by the body.
-- worldCoordinates [in]: the world coordinates of the point.
-- return: the gravity vector in meter/seconds^2
--
function BodyParameters:getGravity(worldCoordinates)
    local radial = self.center - vec3(worldCoordinates) -- directed towards body
    local len2   = radial:len2()
    return (self.GM/len2) * radial/math.sqrt(len2)
end
-- end of module
return setmetatable(PlanetaryReference,
                    { __call = function(_,...)
                                    return mkPlanetaryReference(...)
                               end })
end
function Keplers()
    --[[ 
  Provides methods for computing orbital information for an object
  Usage:
  Kepler = require('autoconf.custom.kepler')
  alioth = Kepler({ GM=157470826617,
                    bodyId=2,
                    center={x=-8.000,y=-8.000,z=-126303.000},
                    name='Alioth',
                    planetarySystemId=0,
                    radius=126068
                  })
  altitude = 6000
  position = '::pos{0,2,0,0,6000}'
  e, o     = alioth:escapeAndOrbitalSpeed(altitude)
  orbit    = alioth:orbitalParameters(position, {0, o+1, 0})
  print("Eccentricity " .. orbit.eccentricity)
  print("Perihelion " .. orbit.periapsis.altitude)
  print("Max. speed " .. orbit.periapsis.speed)
  print("Circular orbit speed " .. orbit.periapsis.circularOrbitSpeed)
  print("Aphelion "  .. orbit.apoapsis.altitude)
  print("Min. speed " .. orbit.apoapsis.speed)
  print("Orbital period " .. orbit.period)
  --- output:
    Eccentricity 0.0018324307017878
    Perihelion 6000.0
    Max. speed 1092.9462297033
    Circular orbit speed 1091.9462297033
    Aphelion 6484.8994605062
    Min. speed 1088.9480596194
    Orbital period 762.02818214049
  Methods:
    Kepler:escapeAndOrbitalSpeed - for a given celestial body and altitude.
    Kepler:orbitalParameters - for a given massless object and a celestial body.
  Description
  The motion of an object in the vicinity of substantially larger mass is
  in the domain of the "2-body problem". By assuming the object whose motion
  is of interest is of negligable mass simplifies the calculations of:
  the speed to escape the body, the speed of a circular orbit, and the
  parameters defining the orbit of the object (or the lack of orbit as the
  case may be).
  Orbital Parameters:
     periapsis - the closest approach to the planet
      apoapsis - the furthest point from the planet if in orbit (otherwise nil)
  eccentricity - 0 for circular orbits
                <1 for elliptical orbits
                 1 for parabiolic trajectory
                >1 for hyperbolic trajectory
        period - time (in seconds) to complete an orbit
  Also See: planetref.lua
]]--
local vec3       = require('cpml.vec3')
local PlanetRef  = PlanetRef()
local function isString(s) return type(s)   == 'string' end
local function isTable(t)  return type(t)   == 'table'  end
local function float_eq(a,b)
    if a == 0 then return math.abs(b) < 1e-09 end
    if b == 0 then return math.abs(a) < 1e-09 end
    return math.abs(a - b) < math.max(math.abs(a),math.abs(b))*epsilon
end
Kepler = {}
Kepler.__index = Kepler
--
-- escapeAndOrbitalSpeed - speed required to escape and for a circular orbit
-- altitude [in]: the height of the orbit in meters above "sea-level"
-- return: the speed in m/s needed to escape the celestial body and to orbit it.
--
function Kepler:escapeAndOrbitalSpeed(altitude)
    assert(self.body)
    -- P = -GMm/r and KE = mv^2/2 (no lorentz factor used)
    -- mv^2/2 = GMm/r
    -- v^2 = 2GM/r
    -- v = sqrt(2GM/r1)
    local distance = altitude + self.body.radius
    if not float_eq(distance, 0) then
        local orbit = math.sqrt(self.body.GM/distance)
        return math.sqrt(2)*orbit, orbit
    end
    return nil, nil
end
--
-- orbitalParameters: determine the orbital elements for a two-body system.
-- overload [in]: the world coordinates or map coordinates of a massless object.
-- velocity [in]: The velocity of the massless point object in m/s.
-- return: the 6 orbital elements for the massless object.
--
function Kepler:orbitalParameters(overload, velocity)
    assert(self.body)
    assert(isTable(overload) or isString(overload))
    assert(isTable(velocity))
    local pos = (isString(overload) or PlanetRef.isMapPosition(overload)) and
                            self.body:convertToWorldCoordinates(overload) or
                vec3(overload)
    local v   = vec3(velocity)
    local r   = pos - self.body.center
    local v2  = v:len2()
    local d   = r:len()
    local mu  = self.body.GM
    local e   = ((v2 - mu/d)*r - r:dot(v)*v)/mu
    local a   = mu/(2*mu/d - v2)
    local ecc = e:len()
    local dir = e:normalize()
    local pd  = a*(1-ecc)
    local ad  = a*(1+ecc)
    local per = pd*dir + self.body.center
    local apo = ecc <= 1 and -ad*dir + self.body.center or nil
    local trm = math.sqrt(a*mu*(1-ecc*ecc))        
    local Period = apo and 2*math.pi*math.sqrt(a^3/mu)
    -- These are great and all, but, I need more.
    local trueAnomaly = math.acos((e:dot(r))/(ecc*d))
    if r:dot(v) < 0 then
        trueAnomaly = -(trueAnomaly - 2*math.pi)
    end        
    -- Apparently... cos(EccentricAnomaly) = (cos(trueAnomaly) + eccentricity)/(1 + eccentricity * cos(trueAnomaly))
    local EccentricAnomaly = math.acos((math.cos(trueAnomaly) + ecc)/(1 + ecc * math.cos(trueAnomaly)))
    -- Then.... apparently if this is below 0, we should add 2pi to it
    -- I think also if it's below 0, we're past the apoapsis?
    local timeTau = EccentricAnomaly
    if timeTau < 0 then
        timeTau = timeTau + 2*math.pi
    end
    -- So... time since periapsis...
    -- Is apparently easy if you get mean anomly.  t = M/n where n is mean motion, = 2*pi/Period
    
    
    local MeanAnomaly = timeTau - ecc * math.sin(timeTau)
    local TimeSincePeriapsis = MeanAnomaly/(2*math.pi/Period)
    --system.print(MeanAnomaly .. " - " .. TimeSincePeriapsis .. " - " .. Period .. " - " .. EccentricAnomaly .. " - " .. timeTau .. " - " .. trueAnomaly)
    -- Mean anom is 0 at periapsis, positive before it... and positive after it.
    -- I guess this is why I needed to use timeTau and not EccentricAnomaly here
    
    local TimeToPeriapsis = Period - TimeSincePeriapsis
    local TimeToApoapsis = TimeToPeriapsis + Period/2
    if trueAnomaly - math.pi > 0 then -- TBH I think something's wrong in my formulas because I needed this.
        TimeToPeriapsis = TimeSincePeriapsis
        TimeToApoapsis = TimeToPeriapsis + Period/2
    end
    if TimeToApoapsis > Period then
        TimeToApoapsis = TimeToApoapsis - Period
    end
    return { periapsis       = { position           = per,
                                 speed              = trm/pd,
                                 circularOrbitSpeed = math.sqrt(mu/pd),
                                 altitude           = pd - self.body.radius},
             apoapsis        = apo and
                               { position           = apo,
                                 speed              = trm/ad,
                                 circularOrbitSpeed = math.sqrt(mu/ad),
                                 altitude           = ad - self.body.radius},
             currentVelocity = v,
             currentPosition = pos,
             eccentricity    = ecc,
             period          = Period,
             eccentricAnomaly = EccentricAnomaly,
             meanAnomaly = MeanAnomaly,
             timeToPeriapsis = TimeToPeriapsis,
             timeToApoapsis = TimeToApoapsis
           }
end

local function new(bodyParameters)
    local params = PlanetRef.BodyParameters(bodyParameters.planetarySystemId,
                                            bodyParameters.bodyId,
                                            bodyParameters.radius,
                                            bodyParameters.center,
                                            bodyParameters.GM)
    return setmetatable({body = params}, Kepler)
end
return setmetatable(Kepler, { __call = function(_,...) return new(...) end })
end
function Kinematics()
    --[[ 
  DualUniverse kinematic equations
  Author: JayleBreak
  Usage (unit.start):
  Kinematics = require('autoconf.custom.kinematics')
  Methods:
   computeAccelerationTime - "relativistic" version of t = (vf - vi)/a
   computeDistanceAndTime - Return distance & time needed to reach final speed.
   computeTravelTime - "relativistic" version of t=(sqrt(2ad+v^2)-v)/a
  Description
  DualUniverse increases the effective mass of constructs as their absolute
  speed increases by using the "lorentz" factor (from relativity) as the scale
  factor.  This results in an upper bound on the absolute speed of constructs
  (excluding "warp" drive) that is set to 30 000 KPH (8 333 MPS). This module
  provides utilities for computing some physical quantities taking this
  scaling into account.
]]--
local Kinematic = {} -- just a namespace
local C       = 30000000/3600
local C2      = C*C
local ITERATIONS = 100 -- iterations over engine "warm-up" period
local function lorentz(v) return 1/math.sqrt(1 - v*v/C2) end
--
-- computeAccelerationTime - "relativistic" version of t = (vf - vi)/a
-- initial      [in]: initial (positive) speed in meters per second.
-- acceleration [in]: constant acceleration until 'finalSpeed' is reached.
-- final        [in]: the speed at the end of the time interval.
-- return: the time in seconds spent in traversing the distance
--
function Kinematic.computeAccelerationTime(initial, acceleration, final)
    -- The low speed limit of following is: t=(vf-vi)/a (from: vf=vi+at)
    local k1 = C*math.asin(initial/C)
    return (C * math.asin(final/C) - k1)/acceleration
end
--
-- computeDistanceAndTime - Return distance & time needed to reach final speed.
-- initial[in]:     Initial speed in meters per second.
-- final[in]:       Final speed in meters per second.
-- restMass[in]:    Mass of the construct at rest in Kg.
-- thrust[in]:      Engine's maximum thrust in Newtons.
-- t50[in]:         (default: 0) Time interval to reach 50% thrust in seconds.
-- brakeThrust[in]: (default: 0) Constant thrust term when braking.
-- return: Distance (in meters), time (in seconds) required for change.
--
function Kinematic.computeDistanceAndTime(initial,
                                          final,
                                          restMass,
                                          thrust,
                                          t50,
                                          brakeThrust)
    -- This function assumes that the applied thrust is colinear with the
    -- velocity. Furthermore, it does not take into account the influence
    -- of gravity, not just in terms of its impact on velocity, but also
    -- its impact on the orientation of thrust relative to velocity.
    -- These factors will introduce (usually) small errors which grow as
    -- the length of the trip increases.
    t50            = t50 or 0
    brakeThrust    = brakeThrust or 0 -- usually zero when accelerating
    local tau0     = lorentz(initial)
    local speedUp  = initial <= final
    local a0       = thrust * (speedUp and 1 or -1)/restMass
    local b0       = -brakeThrust/restMass
    local totA     = a0+b0
    if speedUp and totA <= 0 or not speedUp and totA >= 0 then
        return -1, -1 -- no solution
    end
    local distanceToMax, timeToMax = 0, 0
    -- If, the T50 time is set, then assume engine is at zero thrust and will
    -- reach full thrust in 2*T50 seconds. Thrust curve is given by:
    -- Thrust: F(z)=(a0*(1+sin(z))+2*b0)/2 where z=pi*(t/t50 - 1)/2
    -- Acceleration is given by F(z)/m(z) where m(z) = m/sqrt(1-v^2/c^2)
    -- or v(z)' = (a0*(1+sin(z))+2*b0)*sqrt(1-v(z)^2/c^2)/2
    if a0 ~= 0 and t50 > 0 then
        -- Closed form solution for velocity exists:
        -- v(t) = -c*tan(w)/sqrt(tan(w)^2+1) => w = -asin(v/c)
        -- w=(pi*t*(a0/2+b0)-a0*t50*sin(pi*t/2/t50)+*pi*c*k1)/pi/c
        -- @ t=0, v(0) = vi
        -- pi*c*k1/pi/c = -asin(vi/c)
        -- k1 = asin(vi/c)
        local k1  = math.asin(initial/C)
        local c1  = math.pi*(a0/2+b0)
        local c2  = a0*t50
        local c3  = C*math.pi
        local v = function(t)
            local w  = (c1*t - c2*math.sin(math.pi*t/2/t50) + c3*k1)/c3
            local tan = math.tan(w)
            return C*tan/math.sqrt(tan*tan+1)
        end
        local speedchk = speedUp and function(s) return s >= final end or
                                     function(s) return s <= final end
        timeToMax  = 2*t50
        if speedchk(v(timeToMax)) then
            local lasttime = 0
            while math.abs(timeToMax - lasttime) > 0.5 do
                local t = (timeToMax + lasttime)/2
                if speedchk(v(t)) then
                    timeToMax = t 
                else
                    lasttime = t
                end
            end
        end
        -- There is no closed form solution for distance in this case.
        -- Numerically integrate for time t=0 to t=2*T50 (or less)
        local lastv = initial
        local tinc  = timeToMax/ITERATIONS
        for step = 1, ITERATIONS do
            local speed = v(step*tinc)
            distanceToMax = distanceToMax + (speed+lastv)*tinc/2
            lastv = speed
        end
        if timeToMax < 2*t50 then
            return distanceToMax, timeToMax
        end
        initial     = lastv
    end
    -- At full thrust, acceleration only depends on the Lorentz factor:
    -- v(t)' = (F/m(v)) = a*sqrt(1-v(t)^2/c^2) where a = a0+b0
    -- -> v = c*sin((at+k1)/c)
    -- @ t=0, v=vi: k1 = c*asin(vi/c)
    -- -> t = (c*asin(v/c) - k1)/a
    -- x(t)' = c*sin((at+k1)/c)
    -- x = k2 - c^2 cos((at+k1)/c)/a
    -- @ t=0, x=0: k2 = c^2 * cos(k1/c)/a
    local k1       = C*math.asin(initial/C)
    local time     = (C * math.asin(final/C) - k1)/totA
    local k2       = C2 *math.cos(k1/C)/totA
    local distance = k2 - C2 * math.cos((totA*time + k1)/C)/totA
    return distance+distanceToMax, time+timeToMax
end
--
-- computeTravelTime - "relativistic" version of t=(sqrt(2ad+v^2)-v)/a
-- initialSpeed [in]: initial (positive) speed in meters per second
-- acceleration [in]: constant acceleration until 'distance' is traversed
-- distance [in]: the distance traveled in meters
-- return: the time in seconds spent in traversing the distance
--
function Kinematic.computeTravelTime(initial, acceleration, distance)
    -- The low speed limit of following is: t=(sqrt(2ad+v^2)-v)/a
    -- (from: d=vt+at^2/2)
    if distance == 0 then return 0 end
    if acceleration > 0 then
        local k1       = C*math.asin(initial/C)
        local k2       = C2*math.cos(k1/C)/acceleration
        return (C*math.acos(acceleration*(k2 - distance)/C2) - k1)/acceleration
    end
    assert(initial > 0, 'Acceleration and initial speed are both zero.')
    return distance/initial
end
function Kinematic.lorentz(v) return lorentz(v) end
return Kinematic
end
PlanetaryReference = PlanetRef()
galaxyReference = PlanetaryReference(Atlas())
Kinematic = Kinematics()
Kep = Keplers()
function getDistanceDisplayString(distance)
    local su = distance > 100000
    local result = ""
    if su then
        -- Convert to SU
        result = round(distance/1000/200,1) .. " SU"
    else
        -- Convert to KM
        result = round(distance/1000,1) .. " KM"
    end

    return result
end

PlanetaryReference = PlanetRef()
galaxyReference = PlanetaryReference(Atlas())

MapScreenButtons = {}
MapScreenMouseX = 0
MapScreenMouseY = 0
MapScreenMouseDown = false
MapScreenButtonSelected = 0
local worldPos = vec3(core.getConstructWorldPos())
local locX = (worldPos.x/400000)
local locY = (worldPos.y/400000)*(-1)
local destX = 0
local destY = 0
local sudistance = 0
local loc = vec3(core.getConstructWorldPos())
local ion = galaxyReference[0][120]  ---uses Atlas functions
local thades = vec3(29165536.000, 10865536.000, 65536.000)
local sinnen = vec3(58665536.000, 29665536.000, 58165536.000)
local alioth = galaxyReference[0][2]    ---uses Atlas functions
local madis = vec3(17465536.000, 22665536.000, -34464.000)
local jago = vec3(-94134464.000, 12765536.000, -3634464.000)
local symeon = vec3(14165536.000, -85634464.000, -934464.000)
local lacobus = vec3(98865536.000, -13534464.000, -934464.000)
local teoma = vec3(80865536.000, 54665536.000, -934464.000)
local feli = vec3(-43534464.000, 22565536.000, -48934464.000)
local talemai = vec3(-13234464.000, 55765536.000, 465536.000)
local sicari = vec3(52765536.000, 27165536.000, 52065536.000)
local distion = math.floor(ion:getDistance(loc)/200000)   ---uses getDistance functions----
local distthades = string.format("%.2f", math.sqrt((loc.x-thades.x)^2+(loc.y-thades.y)^2+(loc.z-thades.z)^2)/200000)
local distalioth = math.floor(alioth:getDistance(loc)/200000)   ---uses getDistance functions----
local distmadis = string.format("%.2f", math.sqrt((loc.x-madis.x)^2+(loc.y-madis.y)^2+(loc.z-madis.z)^2)/200000)
local distjago = string.format("%.2f", math.sqrt((loc.x-jago.x)^2+(loc.y-jago.y)^2+(loc.z-jago.z)^2)/200000)
local distlacobus = string.format("%.2f", math.sqrt((loc.x-lacobus.x)^2+(loc.y-lacobus.y)^2+(loc.z-lacobus.z)^2)/200000)
local distteoma = string.format("%.2f", math.sqrt((loc.x-teoma.x)^2+(loc.y-teoma.y)^2+(loc.z-teoma.z)^2)/200000)
local distsymeon = string.format("%.2f", math.sqrt((loc.x-symeon.x)^2+(loc.y-symeon.y)^2+(loc.z-symeon.z)^2)/200000)
local distfeli = string.format("%.2f", math.sqrt((loc.x-feli.x)^2+(loc.y-feli.y)^2+(loc.z-feli.z)^2)/200000)
local distsinnen = string.format("%.2f", math.sqrt((loc.x-sinnen.x)^2+(loc.y-sinnen.y)^2+(loc.z-sinnen.z)^2)/200000)
local disttalemai = string.format("%.2f", math.sqrt((loc.x-talemai.x)^2+(loc.y-talemai.y)^2+(loc.z-talemai.z)^2)/200000)
local distsicari = string.format("%.2f", math.sqrt((loc.x-sicari.x)^2+(loc.y-sicari.y)^2+(loc.z-sicari.z)^2)/200000)



      for i = 1,1 do
   local button = {id = ("b"..1), enabled=true, td="<td>", top=2/100, bottom=13/100, left=1/100, right=28/100}
    table.insert(MapScreenButtons, button)
end
  for i = 2,2 do
   local button = {id = ("b"..2), enabled=true, td="<td>", top=15/100, bottom=26/100, left=1/100, right=30/100}
    table.insert(MapScreenButtons, button)
end
    for i = 3,3 do
    local button = {id = ("b"..3), enabled=true, td="<td>", top=27/100, bottom=38/100, left=1/100, right=28/100}
      table.insert(MapScreenButtons, button)
end
    for i = 4,4 do   
    local button = {id = ("b"..4), enabled=true, td="<td>", top=39/100, bottom=50/100, left=1/100, right=28/100}
      table.insert(MapScreenButtons, button)
end
    for i = 5,5 do   
    local button = {id = ("b"..5), enabled=true, td="<td>", top=51/100, bottom=62/100, left=1/100, right=28/100}
      table.insert(MapScreenButtons, button)
end
    for i = 6,6 do   
    local button = {id = ("b"..6), enabled=true, td="<td>", top=64/100, bottom=75/100, left=1/100, right=28/100}
      table.insert(MapScreenButtons, button)
end
    for i = 7,7 do   
    local button = {id = ("b"..7), enabled=true, td="<td>", top=2/100, bottom=13/100, left=75/100, right=100/100}
      table.insert(MapScreenButtons, button)
end
    for i = 8,8 do   
    local button = {id = ("b"..8), enabled=true, td="<td>", top=15/100, bottom=26/100, left=75/100, right=100/100}
      table.insert(MapScreenButtons, button)
end
    for i = 9,9 do   
    local button = {id = ("b"..9), enabled=true, td="<td>", top=27/100, bottom=38/100, left=75/100, right=100/100}
      table.insert(MapScreenButtons, button)
end
    for i = 10,10 do   
    local button = {id = ("b"..10), enabled=true, td="<td>", top=39/100, bottom=50/100, left=75/100, right=100/100}
      table.insert(MapScreenButtons, button)
end
    for i = 11,11 do   
    local button = {id = ("b"..11), enabled=true, td="<td>", top=51/100, bottom=62/100, left=75/100, right=100/100}
      table.insert(MapScreenButtons, button)
end
    for i = 12,12 do   
    local button = {id = ("b"..12), enabled=true, td="<td>", top=64/100, bottom=75/100, left=75/100, right=100/100}
      table.insert(MapScreenButtons, button)
end
    for i = 13,13 do   
    local button = {id = ("b"..13), enabled=true, td="<td>", top=90/100, bottom=100/100, left=1/100, right=18/100}
    table.insert(MapScreenButtons, button)
end
function evaluateButtons()
  local selected = 0
  
  if #MapScreenButtons >= 1 then
 -- Set button styles
       for i, button in ipairs(MapScreenButtons) do
            if button.left < MapScreenMouseX and MapScreenMouseX < button.right and button.top < MapScreenMouseY and MapScreenMouseY < button.bottom then
                if MapScreenMouseDown and MapScreenButtonSelected == i then
                end
                selected = i
            end
            if not button.enabled then
            end

        end
  end
  return selected
end

function onButtonDown(buttonNo)
  local button = MapScreenButtons[buttonNo]    
  if not button or not button.enabled then
	return
  end
end
function onButtonUp(buttonNo)
  local button = MapScreenButtons[buttonNo]    
  if not button or not button.enabled then
    return
  end
function onClick(buttonNo)
  local button = MapScreenButtons[buttonNo]    
  if not button or not button.enabled then
    return
  end
end
  if buttonNo == 1 then
destX = 0
destY = 0
sudistance = distalioth
  elseif buttonNo == 2 then
destX = 43
destY = -56
sudistance = distmadis
  elseif buttonNo == 3 then
destX = 73
destY = -27
sudistance = distthades        
  elseif buttonNo == 4 then
destX = -33
destY = -139
sudistance = disttalemai
  elseif buttonNo == 5 then
destX = -109
destY = -56
sudistance = distfeli        
  elseif buttonNo == 6 then
destX = 131
destY = -68
sudistance = distsicari        
  elseif buttonNo == 7 then
destX = 35
destY = 214
sudistance = distsymeon
  elseif buttonNo == 8 then
destX = 146
destY = -74
sudistance = distsinnen        
  elseif buttonNo == 9 then
destX = -235
destY = -32
sudistance = distjago       
  elseif buttonNo == 10 then
destX = 202
destY = -137
sudistance = distteoma        
  elseif buttonNo == 11 then
destX = 7
destY = 247
sudistance = distion        
  elseif buttonNo == 12 then
destX = 247
destY = 34
sudistance = distlacobus
  elseif buttonNo == 13 then
  unit.exit()
  end
end

function updateScreen() 
warpmath = math.floor(math.floor(core.getConstructMass()/ 1000)  * sudistance * 0.00025)
html= ([[
<svg class="bootstrap" viewBox="0 0 1024 1024" style="width:100%; height:100%"><circle cx="500" cy="500" r="400" stroke="darkgreen" stroke-width="3" transform=""></circle><circle cx="500" cy="500" r="350" stroke="darkgreen" stroke-width="3" transform="" stroke-opacity="0.2"></circle><circle cx="500" cy="500" r="300" stroke="darkgreen" stroke-width="3" transform=""></circle><circle cx="500" cy="500" r="250" stroke="darkgreen" stroke-width="3" transform="" stroke-opacity="0.2"></circle><circle cx="500" cy="500" r="200" stroke="darkgreen" stroke-width="3" transform=""></circle><circle cx="500" cy="500" r="150" stroke="darkgreen" stroke-width="3" transform="" stroke-opacity="0.2"></circle><circle cx="500" cy="500" r="100" stroke="lightblue" stroke-width="3" transform=""></circle><circle cx="500" cy="500" r="50" stroke="lightblue" stroke-width="3" transform="" stroke-opacity="0.2"></circle><circle cx="500" cy="500" r="20" stroke="Orange" stroke-width="2" transform=""></circle><text x="510" y="510" fill="Yellow">Helios</text><circle cx="-0.00" cy="0" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="-0.00" y="0" transform="translate(500,480)" fill="white" font-size="20">Alioth</text><circle cx="7.16" cy="247.59" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="7.16" y="247.59" transform="translate(480,480)" fill="white" font-size="20">Ion</text><circle cx="35.41" cy="214.09" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="35.41" y="214.09" transform="translate(500,480)" fill="white" font-size="20">Symeon</text><circle cx="-33.09" cy="-139.41" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="-33.09" y="-139.41" transform="translate(500,480)" fill="white" font-size="20">Talemai</text><circle cx="202.16" cy="-136.66" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="202.16" y="-136.66" transform="translate(500,480)" fill="white" font-size="20">Teoma</text><circle cx="247.16" cy="33.84" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="247.16" y="33.84" transform="translate(500,480)" fill="white" font-size="20">Lacobus</text><circle cx="-108.84" cy="-56.41" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="-108.84" y="-56.41" transform="translate(500,480)" fill="white" font-size="20">Feli</text><circle cx="72.91" cy="-27.16" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="72.91" y="-27.16" transform="translate(500,485)" fill="white" font-size="20">Thades</text><circle cx="43.66" cy="-56.66" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="43.66" y="-56.66" transform="translate(500,480)" fill="white" font-size="20">Madis</text><circle cx="-235.34" cy="-31.91" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="-235.34" y="-31.91" transform="translate(500,480)" fill="white" font-size="20">Jago</text><circle cx="131.91" cy="-67.91" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="131.91" y="-67.91" transform="translate(475,480)" fill="white" font-size="20">Sicari</text><circle cx="146.66" cy="-74.16" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="146.66" y="-74.16" transform="translate(515,480)" fill="white" font-size="20">Sinnen</text>
<line stroke-linecap="undefined" stroke-linejoin="undefined" id="svg_1" y2="]]..destY..[[" x2="]]..destX..[[" y1="]]..locY..[[" x1="]]..locX..[[" transform="translate(500,500)" stroke-width="5" stroke="#ff0000" fill="none"/>        
<circle cx="]]..locX..[[" cy="]]..locY..[[" r="3" stroke="black" stroke-width="1" fill="limegreen" transform="translate(500,500)"></circle>
<text x="]]..locX..[[" y="]]..locY..[[" transform="translate(500,500)" 
fill="limegreen" font-size= "4.5vh" font-weight= "bold">//SHIP POSITION</text>
</svg>
<svg class="bootstrap" viewBox="0 0 1024 612" style="width:100%; height:100%">
 <g>
  <title>Layer 1</title>
  <g id="svg_24">
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_8" y="70" x="55" stroke-width="0" fill="Yellow">Alioth  :]]..distalioth..[[ SU</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_14" y="170" x="55" stroke-width="0" fill="Yellow">Madis  :]]..distmadis..[[ SU</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_17" y="270" x="55" stroke-width="0" fill="Yellow">Thades  :]]..distthades..[[ SU</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_20" y="370" x="55" stroke-width="0" fill="Yellow">Talemai  :]]..disttalemai..[[ SU</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_23" y="470" x="55" stroke-width="0" fill="Yellow">Feli  :]]..distfeli..[[ SU</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_26" y="570" x="55" stroke-width="0" fill="Yellow">Sicari  :]]..distsicari..[[ SU</text>
   <g id="svg_12">
    <rect rx="10" id="svg_1" height="50" width="250" y="30" x="15" stroke-width="20" stroke="#00ff00" fill="none"/>
    <rect rx="10" id="svg_3" height="50" width="250" y="105" x="15" stroke-width="20" stroke="#00ff00" fill="none"/>
    <rect rx="10" id="svg_7" height="50" width="250" y="180" x="15" stroke-width="20" stroke="#00ff00" fill="none"/>
    <rect rx="10" id="svg_9" height="50" width="250" y="255" x="15" stroke-width="20" stroke="#00ff00" fill="none"/>
    <rect rx="10" id="svg_10" height="50" width="250" y="330" x="15" stroke-width="20" stroke="#00ff00" fill="none"/>
    <rect rx="10" id="svg_11" height="50" width="250" y="405" x="15" stroke-width="20" stroke="#00ff00" fill="none"/>
   </g>
  </g>
  <g id="svg_40">
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_25" y="70" x="997.163642" stroke-width="0" fill="Yellow">Symeon  :]]..distsymeon..[[ SU</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_27" y="170" x="997.163642" stroke-width="0" fill="Yellow">Sinnen  :]]..distsinnen..[[ SU</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_28" y="270" x="997.163642" stroke-width="0" fill="Yellow">Jago  :]]..distjago..[[ SU</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_30" y="370" x="997.163642" stroke-width="0" fill="Yellow">Teoma  :]]..distteoma..[[ SU</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_31" y="470" x="997.163642" stroke-width="0" fill="Yellow">Ion  :]]..distion..[[ SU</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_32" y="570" x="997.163642" stroke-width="0" fill="Yellow">Lacobus  :]]..distlacobus..[[ SU</text>
   <g id="svg_39">
    <rect rx="10" id="svg_33" height="50" width="250" y="30" x="760" stroke-width="20" stroke="#00ff00" fill="none"/>
    <rect rx="10" id="svg_34" height="50" width="250" y="105" x="760" stroke-width="20" stroke="#00ff00" fill="none"/>
    <rect rx="10" id="svg_35" height="50" width="250" y="180" x="760" stroke-width="20" stroke="#00ff00" fill="none"/>
    <rect rx="10" id="svg_36" height="50" width="250" y="255" x="760" stroke-width="20" stroke="#00ff00" fill="none"/>
    <rect rx="10" id="svg_37" height="50" width="250" y="330" x="760" stroke-width="20" stroke="#00ff00" fill="none"/>
    <rect rx="10" id="svg_38" height="50" width="250" y="405" x="760" stroke-width="20" stroke="#00ff00" fill="none"/>
   </g>
  </g>
 </g>
 <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="30" id="svg_32" y="700" x="20" stroke-width="0" fill="LightBlue">Est. Warp Cost: ]]..warpmath..[[</text>
  <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="30" id="svg_32" y="750" x="20" stroke-width="0" fill="LightBlue">Construct Weight: ]]..math.floor(core.getConstructMass()/ 1000)..[[ tons</text>
    
</svg>    
    ]])
screen.setHTML(html)
end
unit.setTimer("spacemap",.08)


 

Json Paste

{"slots":{"0":{"name":"screen","type":{"events":[],"methods":[]}},"1":{"name":"core","type":{"events":[],"methods":[]}},"2":{"name":"slot3","type":{"events":[],"methods":[]}},"3":{"name":"slot4","type":{"events":[],"methods":[]}},"4":{"name":"slot5","type":{"events":[],"methods":[]}},"5":{"name":"slot6","type":{"events":[],"methods":[]}},"6":{"name":"slot7","type":{"events":[],"methods":[]}},"7":{"name":"slot8","type":{"events":[],"methods":[]}},"8":{"name":"slot9","type":{"events":[],"methods":[]}},"9":{"name":"slot10","type":{"events":[],"methods":[]}},"-1":{"name":"unit","type":{"events":[],"methods":[]}},"-2":{"name":"system","type":{"events":[],"methods":[]}},"-3":{"name":"library","type":{"events":[],"methods":[]}}},"handlers":[{"code":"MapScreenMouseX = x\nMapScreenMouseY = y\nMapScreenMouseDown = false\nlocal buttonNo = evaluateButtons()\nif MapScreenButtonSelected > 0 and MapScreenButtonSelected == buttonNo then\n    onButtonUp(buttonNo)\n    onClick(buttonNo)\nend\nMapScreenButtonSelected = -buttonNo","filter":{"args":[{"variable":"*"},{"variable":"*"}],"signature":"mouseUp(x,y)","slotKey":"0"},"key":"0"},{"code":"MapScreenMouseX = x\nMapScreenMouseY = y\nMapScreenMouseDown = true\nMapScreenButtonSelected = evaluateButtons()\nonButtonDown(MapScreenButtonSelected)\n","filter":{"args":[{"variable":"*"},{"variable":"*"}],"signature":"mouseDown(x,y)","slotKey":"0"},"key":"1"},{"code":"function Atlas()\n        return {\n    [0] = {\n      [1]={\n        GM=6930729684,\n        bodyId=1,\n        center={x=17465536.000,y=22665536.000,z=-34464.000},\n        name='Madis',\n        planetarySystemId=0,\n        radius=44300\n      },\n      [2]={\n        GM=157470826617,\n        bodyId=2,\n        center={x=-8.000,y=-8.000,z=-126303.000},\n        name='Alioth',\n        planetarySystemId=0,\n        radius=126068\n      },\n      [3]={\n        GM=11776905000,\n        bodyId=3,\n        center={x=29165536.000,y=10865536.000,z=65536.000},\n        name='Thades',\n        planetarySystemId=0,\n        radius=49000\n      },\n      [4]={\n        GM=14893847582,\n        bodyId=4,\n        center={x=-13234464.000,y=55765536.000,z=465536.000},\n        name='Talemai',\n        planetarySystemId=0,\n        radius=57450\n      },\n      [5]={\n        GM=16951680000,\n        bodyId=5,\n        center={x=-43534464.000,y=22565536.000,z=-48934464.000},\n        name='Feli',\n        planetarySystemId=0,\n        radius=60000\n      },\n      [6]={\n        GM=10502547741,\n        bodyId=6,\n        center={x=52765536.000,y=27165538.000,z=52065535.000},\n        name='Sicari',\n        planetarySystemId=0,\n        radius=51100\n      },\n      [7]={\n        GM=13033380591,\n        bodyId=7,\n        center={x=58665538.000,y=29665535.000,z=58165535.000},\n        name='Sinnen',\n        planetarySystemId=0,\n        radius=54950\n      },\n      [8]={\n        GM=18477723600,\n        bodyId=8,\n        center={x=80865538.000,y=54665536.000,z=-934463.940},\n        name='Teoma',\n        planetarySystemId=0,\n        radius=62000\n      },\n      [9]={\n        GM=18606274330,\n        bodyId=9,\n        center={x=-94134462.000,y=12765534.000,z=-3634464.000},\n        name='Jago',\n        planetarySystemId=0,\n        radius=61590\n      },\n      [10]={\n        GM=78480000,\n        bodyId=10,\n        center={x=17448118.224,y=22966846.286,z=143078.820},\n        name='Madis Moon 1',\n        planetarySystemId=0,\n        radius=10000\n      },\n      [11]={\n        GM=237402000,\n        bodyId=11,\n        center={x=17194626.000,y=22243633.880,z=-214962.810},\n        name='Madis Moon 2',\n        planetarySystemId=0,\n        radius=11000\n      },\n      [12]={\n        GM=265046609,\n        bodyId=12,\n        center={x=17520614.000,y=22184730.000,z=-309989.990},\n        name='Madis Moon 3',\n        planetarySystemId=0,\n        radius=15005\n      },\n      [21]={\n        GM=2118960000,\n        bodyId=21,\n        center={x=457933.000,y=-1509011.000,z=115524.000},\n        name='Alioth Moon 1',\n        planetarySystemId=0,\n        radius=30000\n      },\n      [22]={\n        GM=2165833514,\n        bodyId=22,\n        center={x=-1692694.000,y=729681.000,z=-411464.000},\n        name='Alioth Moon 4',\n        planetarySystemId=0,\n        radius=30330\n      },\n      [26]={\n        GM=68234043600,\n        bodyId=26,\n        center={x=-1404835.000,y=562655.000,z=-285074.000},\n        name='Sanctuary',\n        planetarySystemId=0,\n        radius=83400\n      },\n      [30]={\n        GM=211564034,\n        bodyId=30,\n        center={x=29214402.000,y=10907080.695,z=433858.200},\n        name='Thades Moon 1',\n        planetarySystemId=0,\n        radius=14002\n      },\n      [31]={\n        GM=264870000,\n        bodyId=31,\n        center={x=29404193.000,y=10432768.000,z=19554.131},\n        name='Thades Moon 2',\n        planetarySystemId=0,\n        radius=15000\n      },\n      [40]={\n        GM=141264000,\n        bodyId=40,\n        center={x=-13503090.000,y=55594325.000,z=769838.640},\n        name='Talemai Moon 2',\n        planetarySystemId=0,\n        radius=12000\n      },\n      [41]={\n        GM=106830900,\n        bodyId=41,\n        center={x=-12800515.000,y=55700259.000,z=325207.840},\n        name='Talemai Moon 3',\n        planetarySystemId=0,\n        radius=11000\n      },\n      [42]={\n        GM=264870000,\n        bodyId=42,\n        center={x=-13058408.000,y=55781856.000,z=740177.760},\n        name='Talemai Moon 1',\n        planetarySystemId=0,\n        radius=15000\n      },\n      [50]={\n        GM=499917600,\n        bodyId=50,\n        center={x=-43902841.780,y=22261034.700,z=-48862386.000},\n        name='Feli Moon 1',\n        planetarySystemId=0,\n        radius=14000\n      },\n      [70]={\n        GM=396912600,\n        bodyId=70,\n        center={x=58969616.000,y=29797945.000,z=57969449.000},\n        name='Sinnen Moon 1',\n        planetarySystemId=0,\n        radius=17000\n      },\n      [100]={\n        GM=13975172474,\n        bodyId=100,\n        center={x=98865536.000,y=-13534464.000,z=-934461.990},\n        name='Lacobus',\n        planetarySystemId=0,\n        radius=55650\n      },\n      [101]={\n        GM=264870000,\n        bodyId=101,\n        center={x=98905288.170,y=-13950921.100,z=-647589.530},\n        name='Lacobus Moon 3',\n        planetarySystemId=0,\n        radius=15000\n      },\n      [102]={\n        GM=444981600,\n        bodyId=102,\n        center={x=99180968.000,y=-13783862.000,z=-926156.400},\n        name='Lacobus Moon 1',\n        planetarySystemId=0,\n        radius=18000\n      },\n      [103]={\n        GM=211503600,\n        bodyId=103,\n        center={x=99250052.000,y=-13629215.000,z=-1059341.400},\n        name='Lacobus Moon 2',\n        planetarySystemId=0,\n        radius=14000\n      },\n      [110]={\n        GM=9204742375,\n        bodyId=110,\n        center={x=14165536.000,y=-85634465.000,z=-934464.300},\n        name='Symeon',\n        planetarySystemId=0,\n        radius=49050\n      },\n      [120]={\n        GM=7135606629,\n        bodyId=120,\n        center={x=2865536.700,y=-99034464.000,z=-934462.020},\n        name='Ion',\n        planetarySystemId=0,\n        radius=44950\n      },\n      [121]={\n        GM=106830900,\n        bodyId=121,\n        center={x=2472916.800,y=-99133747.000,z=-1133582.800},\n        name='Ion Moon 1',\n        planetarySystemId=0,\n        radius=11000\n      },\n      [122]={\n        GM=176580000,\n        bodyId=122,\n        center={x=2995424.500,y=-99275010.000,z=-1378480.700},\n        name='Ion Moon 2',\n        planetarySystemId=0,\n        radius=15000\n      }  \n     }\n    }\n    end\nfunction PlanetRef() \n--[[ \n  Provide coordinate transforms and access to kinematic related parameters\n  Author: JayleBreak\n  Usage (unit.start):\n  PlanetaryReference = require('planetref')\n  galaxyReference = PlanetaryReference(referenceTableSource)\n  helios = galaxyReference[0] -- PlanetaryReference.PlanetarySystem instance\n  alioth = helios[2]          -- PlanetaryReference.BodyParameters instance\n  Methods:\n    PlanetaryReference:getPlanetarySystem - based on planetary system ID.\n    PlanetaryReference.isMapPosition - 'true' if an instance of 'MapPosition'\n    PlanetaryReference.createBodyParameters - for entry into reference table\n    PlanetaryReference.BodyParameters - a class containing a body's information.\n    PlanetaryReference.MapPosition - a class for map coordinates\n    PlanetaryReference.PlanetarySystem - a container for planetary system info.\n    PlanetarySystem:castIntersections - from a position in a given direction.\n    PlanetarySystem:closestBody - to the specified coordinates.\n    PlanetarySystem:convertToBodyIdAndWorldCoordinates - from map coordinates.\n    PlanetarySystem:getBodyParameters - from reference table.\n    PlanetarySystem:getPlanetarySystemId - for the instance.\n    BodyParameters:convertToWorldCoordinates - from map coordinates\n    BodyParameters:convertToMapPosition - from world coordinates\n    BodyParameters:getAltitude - of world coordinates\n    BodyParameters:getDistance - from center to world coordinates\n    BodyParameters:getGravity - at a given position in world coordinates.\n  Description\n  An instance of the 'PlanetaryReference' \"class\" can contain transform and\n  kinematic reference information for all planetary systems in DualUniverse.\n  Each planetary system is identified by a numeric identifier. Currently,\n  the only planetary system, Helios, has the identifier: zero. This \"class\"\n  supports the indexing ('[]') operation which is equivalent to the\n  use of the 'getPlanetarySystem' method. It also supports the 'pairs()'\n  method for iterating over planetary systems.\n\n  An instance of the 'PlanetarySystem' \"class\" contains all reference\n  information for a specific system. It supports the indexing ('[]') and\n  'pairs()' functions which allows iteration over each \"body\" in the\n  system where the key is the numeric body ID. It also supports the\n  'tostring()' method.\n  An instance of the 'BodyParameters' \"class\" contains all reference\n  information for a single celestial \"body\" (a moon or planet). It supports\n  the 'tostring()' method, and contains the data members:\n          planetarySystemId - numeric planetary system ID\n          bodyId            - numeric body ID\n          radius            - radius of the body in meters (zero altitude)\n          center            - world coordinates of the body's center position\n          GM                - the gravitation parameter (g = GM/radius^2)\n  Note that the user is allowed to add custom fields (e.g. body name), but\n  should insure that complex table values have the '__tostring' metamethod\n  implemented.\n  Transform and Kinematics:\n  \"World\" coordinates is a cartesian coordinate system with an origin at an\n  arbitrary fixed point in a planetary system and with distances measured in\n  meters. The coordinates are expressible either as a simple table of 3 values\n  or an instance of the 'vec3' class.  In either case, the planetary system\n  identity is implicit.\n  \"Map\" coordinates is a geographic coordinate system with an origin at the\n  center of an identified (by a numeric value) celestial body which is a\n  member of an identified (also a numeric value) planetary system. Note that\n  the convention that latitude, longitude, and altitude values will be the\n  position's x, y, and z world coordinates in the special case of body ID 0.\n  The kinematic parameters in the reference data permit calculations of the\n  gravitational attraction of the celestial body on other objects.\n  Reference Data:\n  This is an example of reference data with a single entry assigned to\n  planetary system ID 0, and body ID 2 ('Alioth'):\n    referenceTable = {\n          [0] = { [2] = { planetarySystemId = 0,\n                          bodyId = 2,\n                          radius = 126068,\n                          center = vec3({x=-8, y=-8, z=-126303}),\n                          GM = 1.572199+11 } -- as in F=-GMm/r^2\n          }\n      }\n    ref=PlanetaryReference(referenceTable)\n  Collecting Reference Data:\n  A combination of information from the \"Map\" screen in the DU user interface,\n  and values reported by the DU Lua API can be the source of the reference\n  table's data (planetarySystemId, bodyId, and surfaceArea is from the user\n  interface):\n    referenceTable = {}\n    referenceTable[planetarySystemId][bodyId] =\n         PlanetaryReference.createBodyParameters(planetarySystemId,\n                                                 bodyId,\n                                                 surfaceArea,\n                                                 core.getConstructWorldPos(),\n                                                 core.getWorldVertical(),\n                                                 core.getAltitude(),\n                                                 core.g())\n  Adapting Data Sources:\n  Other sources of data can be adapted or converted. An example of adapting a\n  table, defined in the file: 'planets.lua', containing information on a single\n  planetary system and using celestial body name as the key follows (note that\n  a 'name' field is added to the BodyParameters instance transparently after\n  construction, and the '__pairs' meta function is required to support the\n  'closestBody' and '__tostring' methods):\n    ref=PlanetaryReference(\n        {[0] = setmetatable(require('planets'),\n                        { __index = function(bodies, bodyId)\n                             for _,v in pairs(bodies) do\n                                 if v and v.bodyId == bodyId then return v end\n                             end\n                             return nil\n                           end,\n                         __pairs = function(bodies)\n                             return function(t, k)\n                                     local nk, nv = next(t, k)\n                                     if nv then\n                                         local GM = nv.gravity * nv.radius^2\n                                         local bp = BodyParameters(0,\n                                                                   nv.id,\n                                                                   nv.radius,\n                                                                   nv.pos,\n                                                                   GM)\n                                         bp.name = nk\n                                         return nk, bp\n                                    end\n                                    return nk, nv\n                                 end, bodies, nil\n                           end })\n    })\n\n  Converting Data Sources:\n  An instance of 'PlanetaryReference' that has been adapted to a data source\n  can be used to convert that source to simple table. For example,\n  using the adapted instance shown above:\n    load('convertedData=' .. tostring(ref))()\n    newRef=PlanetaryReference(convertedData)\n  Also See: kepler.lua\n  ]]--\n--[[                    START OF LOCAL IMPLEMENTATION DETAILS             ]]--\n-- Type checks\nlocal function isNumber(n)  return type(n)           == 'number' end\nlocal function isSNumber(n) return type(tonumber(n)) == 'number' end\nlocal function isTable(t)   return type(t)           == 'table'  end\nlocal function isString(s)  return type(s)           == 'string' end\nlocal function isVector(v)  return isTable(v)\n                                    and isNumber(v.x and v.y and v.z) end\nlocal function isMapPosition(m) return isTable(m) and isNumber(m.latitude  and\n                                                               m.longitude and\n                                                               m.altitude  and\n                                                               m.bodyId    and\n                                                               m.systemId) end\n-- Constants\nlocal deg2rad    = math.pi/180\nlocal rad2deg    = 180/math.pi\nlocal epsilon    = 1e-10\nlocal num        = ' *([+-]?%d+%.?%d*e?[+-]?%d*)'\nlocal posPattern = '::pos{' .. num .. ',' .. num .. ',' ..  num .. ',' ..\n                   num ..  ',' .. num .. '}'\n-- Utilities\nlocal utils  = require('cpml.utils')\nlocal vec3   = require('cpml.vec3')\nlocal clamp  = utils.clamp\nlocal function float_eq(a,b)\n    if a == 0 then return math.abs(b) < 1e-09 end\n    if b == 0 then return math.abs(a) < 1e-09 end\n    return math.abs(a - b) < math.max(math.abs(a),math.abs(b))*epsilon\nend\nlocal function formatNumber(n)\n    local result = string.gsub(\n                    string.reverse(string.format('%.4f',n)),\n                    '^0*%.?','')\n    return result == '' and '0' or string.reverse(result)\nend\nlocal function formatValue(obj)\n    if isVector(obj) then\n        return string.format('{x=%.3f,y=%.3f,z=%.3f}', obj.x, obj.y, obj.z)\n    end\n    if isTable(obj) and not getmetatable(obj) then\n        local list = {}\n        local nxt  = next(obj)\n        if type(nxt) == 'nil' or nxt == 1 then -- assume this is an array\n            list = obj\n        else\n            for k,v in pairs(obj) do\n                local value = formatValue(v)\n                if type(k) == 'number' then\n                    table.insert(list, string.format('[%s]=%s', k, value))\n                else\n                    table.insert(list, string.format('%s=%s',   k, value))\n                end\n            end\n        end\n        return string.format('{%s}', table.concat(list, ','))\n    end\n    if isString(obj) then\n        return string.format(\"'%s'\", obj:gsub(\"'\",[[\\']]))\n    end\n    return tostring(obj)\nend\n-- CLASSES\n-- BodyParameters: Attributes of planetary bodies (planets and moons)\nlocal BodyParameters = {}\nBodyParameters.__index = BodyParameters\nBodyParameters.__tostring =\n    function(obj, indent)\n        local sep = indent or ''\n        local keys = {}\n        for k in pairs(obj) do table.insert(keys, k) end\n        table.sort(keys)\n        local list = {}\n        for _, k in ipairs(keys) do\n            local value = formatValue(obj[k])\n            if type(k) == 'number' then\n                table.insert(list, string.format('[%s]=%s', k, value))\n            else\n                table.insert(list, string.format('%s=%s', k, value))\n            end\n        end\n        if indent then\n            return string.format('%s%s',\n                                 indent,\n                                 table.concat(list, ',\\n' .. indent))\n        end\n        return string.format('{%s}', table.concat(list, ','))\n    end\nBodyParameters.__eq = function(lhs, rhs)\n        return lhs.planetarySystemId == rhs.planetarySystemId and\n               lhs.bodyId            == rhs.bodyId            and\n               float_eq(lhs.radius, rhs.radius)               and\n               float_eq(lhs.center.x, rhs.center.x)           and\n               float_eq(lhs.center.y, rhs.center.y)           and\n               float_eq(lhs.center.z, rhs.center.z)           and\n               float_eq(lhs.GM, rhs.GM)\n    end\nlocal function mkBodyParameters(systemId, bodyId, radius, worldCoordinates, GM)\n    -- 'worldCoordinates' can be either table or vec3\n    assert(isSNumber(systemId),\n           'Argument 1 (planetarySystemId) must be a number:' .. type(systemId))\n    assert(isSNumber(bodyId),\n           'Argument 2 (bodyId) must be a number:' .. type(bodyId))\n    assert(isSNumber(radius),\n           'Argument 3 (radius) must be a number:' .. type(radius))\n    assert(isTable(worldCoordinates),\n           'Argument 4 (worldCoordinates) must be a array or vec3.' ..\n           type(worldCoordinates))\n    assert(isSNumber(GM),\n           'Argument 5 (GM) must be a number:' .. type(GM))\n    return setmetatable({planetarySystemId = tonumber(systemId),\n                         bodyId            = tonumber(bodyId),\n                         radius            = tonumber(radius),\n                         center            = vec3(worldCoordinates),\n                         GM                = tonumber(GM) }, BodyParameters)\nend\n-- MapPosition: Geographical coordinates of a point on a planetary body.\nlocal MapPosition = {}\nMapPosition.__index = MapPosition\nMapPosition.__tostring = function(p)\n        return string.format('::pos{%d,%d,%s,%s,%s}',\n                             p.systemId,\n                             p.bodyId,\n                             formatNumber(p.latitude*rad2deg),\n                             formatNumber(p.longitude*rad2deg),\n                             formatNumber(p.altitude))\n    end\nMapPosition.__eq       = function(lhs, rhs)\n        return lhs.bodyId   == rhs.bodyId              and\n               lhs.systemId == rhs.systemId            and\n               float_eq(lhs.latitude,   rhs.latitude)  and\n               float_eq(lhs.altitude,   rhs.altitude)  and\n               (float_eq(lhs.longitude, rhs.longitude) or\n                float_eq(lhs.latitude, math.pi/2)      or\n                float_eq(lhs.latitude, -math.pi/2))\n    end\n-- latitude and longitude are in degrees while altitude is in meters\nlocal function mkMapPosition(overload, bodyId, latitude, longitude, altitude)\n    local systemId = overload -- Id or '::pos{...}' string\n    if isString(overload) and not longitude and not altitude and\n                              not bodyId    and not latitude then\n        systemId, bodyId, latitude, longitude, altitude =\n                                            string.match(overload, posPattern)\n        assert(systemId, 'Argument 1 (position string) is malformed.')\n    else\n        assert(isSNumber(systemId),\n               'Argument 1 (systemId) must be a number:' .. type(systemId))\n        assert(isSNumber(bodyId),\n               'Argument 2 (bodyId) must be a number:' .. type(bodyId))\n        assert(isSNumber(latitude),\n               'Argument 3 (latitude) must be in degrees:' .. type(latitude))\n        assert(isSNumber(longitude),\n               'Argument 4 (longitude) must be in degrees:' .. type(longitude))\n        assert(isSNumber(altitude),\n               'Argument 5 (altitude) must be in meters:' .. type(altitude))\n    end\n    systemId  = tonumber(systemId)\n    bodyId    = tonumber(bodyId)\n    latitude  = tonumber(latitude)\n    longitude = tonumber(longitude)\n    altitude  = tonumber(altitude)\n    if bodyId == 0 then -- this is a hack to represent points in space\n        return setmetatable({latitude  = latitude,\n                             longitude = longitude,\n                             altitude  = altitude,\n                             bodyId    = bodyId,\n                             systemId  = systemId}, MapPosition)\n    end\n    return setmetatable({latitude  = deg2rad*clamp(latitude, -90, 90),\n                         longitude = deg2rad*(longitude % 360),\n                         altitude  = altitude,\n                         bodyId    = bodyId,\n                         systemId  = systemId}, MapPosition)\nend\n-- PlanetarySystem - map body IDs to BodyParameters\nlocal PlanetarySystem = {}\nPlanetarySystem.__index = PlanetarySystem\nPlanetarySystem.__tostring =\n    function (obj, indent)\n        local sep = indent and (indent .. '  ' )\n        local bdylist = {}\n        local keys = {}\n        for k in pairs(obj) do table.insert(keys, k) end\n        table.sort(keys)\n        for _, bi in ipairs(keys) do\n            bdy = obj[bi]\n            local bdys = BodyParameters.__tostring(bdy, sep)\n            if indent then\n                table.insert(bdylist,\n                             string.format('[%s]={\\n%s\\n%s}',\n                                           bi, bdys, indent))\n            else\n                table.insert(bdylist, string.format('  [%s]=%s', bi, bdys))\n            end\n        end\n        if indent then\n            return string.format('\\n%s%s%s',\n                                 indent,\n                                 table.concat(bdylist, ',\\n' .. indent),\n                                 indent)\n        end\n        return string.format('{\\n%s\\n}', table.concat(bdylist, ',\\n'))\n    end\nlocal function mkPlanetarySystem(referenceTable)\n    local atlas = {}\n    local pid\n    for _, v in pairs(referenceTable) do\n        local id = v.planetarySystemId\n        if type(id) ~= 'number' then\n            error('Invalid planetary system ID: ' .. tostring(id))\n        elseif pid and id ~= pid then\n            error('Mismatch planetary system IDs: ' .. id .. ' and '\n                  .. pid)\n        end\n        local bid = v.bodyId\n        if type(bid) ~= 'number' then\n            error('Invalid body ID: ' .. tostring(bid))\n        elseif atlas[bid] then\n            error('Duplicate body ID: ' .. tostring(bid))\n        end\n        setmetatable(v.center, getmetatable(vec3.unit_x))\n        atlas[bid] = setmetatable(v, BodyParameters)\n        pid = id\n    end\n    return setmetatable(atlas, PlanetarySystem)\nend\n-- PlanetaryReference - map planetary system ID to PlanetarySystem\nPlanetaryReference = {}\nlocal function mkPlanetaryReference(referenceTable)\n    return setmetatable({ galaxyAtlas = referenceTable or {} },\n                          PlanetaryReference)\nend\nPlanetaryReference.__index        = \n    function(t,i)\n        if type(i) == 'number' then\n            local system = t.galaxyAtlas[i]\n            return mkPlanetarySystem(system)\n        end\n        return rawget(PlanetaryReference, i)\n    end\nPlanetaryReference.__pairs        =\n    function(obj)\n        return  function(t, k)\n                    local nk, nv = next(t, k)\n                    return nk, nv and mkPlanetarySystem(nv)\n                end, obj.galaxyAtlas, nil\n    end\nPlanetaryReference.__tostring     =\n    function (obj)\n        local pslist = {}\n        for _,ps in pairs(obj or {}) do\n            local psi = ps:getPlanetarySystemId()\n            local pss = PlanetarySystem.__tostring(ps, '    ')\n            table.insert(pslist,\n                         string.format('  [%s]={%s\\n  }', psi, pss))\n        end\n        return string.format('{\\n%s\\n}\\n', table.concat(pslist,',\\n'))\n    end\n--[[                       START OF PUBLIC INTERFACE                       ]]--\n-- PlanetaryReference CLASS METHODS:\n--\n-- BodyParameters - create an instance of BodyParameters class\n-- planetarySystemId  [in]: the body's planetary system ID.\n-- bodyId             [in]: the body's ID.\n-- radius             [in]: the radius in meters of the planetary body.\n-- bodyCenter         [in]: the world coordinates of the center (vec3 or table).\n-- GM                 [in]: the body's standard gravitational parameter.\n-- return: an instance of BodyParameters class.\n--\nPlanetaryReference.BodyParameters = mkBodyParameters\n--\n-- MapPosition - create an instance of the MapPosition class\n-- overload [in]: either a planetary system ID or a position string ('::pos...')\n-- bodyId [in]:   (ignored if overload is a position string) the body's ID.\n-- latitude [in]: (ignored if overload is a position string) the latitude.\n-- longitude [in]:(ignored if overload is a position string) the longitude.\n-- altitude [in]: (ignored if overload is a position string) the altitude.\n-- return: the class instance\n--\nPlanetaryReference.MapPosition    = mkMapPosition\n--\n-- PlanetarySystem - create an instance of PlanetarySystem class\n-- referenceData [in]: a table (indexed by bodyId) of body reference info.\n-- return: the class instance\n--\nPlanetaryReference.PlanetarySystem = mkPlanetarySystem\n--\n-- createBodyParameters - create an instance of BodyParameters class\n-- planetarySystemId  [in]: the body's planetary system ID.\n-- bodyId             [in]: the body's ID.\n-- surfaceArea        [in]: the body's surface area in square meters.\n-- aPosition          [in]: world coordinates of a position near the body.\n-- verticalAtPosition [in]: a vector pointing towards the body center.\n-- altitudeAtPosition [in]: the altitude in meters at the position.\n-- gravityAtPosition  [in]: the magnitude of the gravitational acceleration.\n-- return: an instance of BodyParameters class.\n--\nfunction PlanetaryReference.createBodyParameters(planetarySystemId,\n                                                 bodyId,\n                                                 surfaceArea,\n                                                 aPosition,\n                                                 verticalAtPosition,\n                                                 altitudeAtPosition,\n                                                 gravityAtPosition)\n    assert(isSNumber(planetarySystemId),\n           'Argument 1 (planetarySystemId) must be a number:' ..\n           type(planetarySystemId))\n    assert(isSNumber(bodyId),\n           'Argument 2 (bodyId) must be a number:' .. type(bodyId))\n    assert(isSNumber(surfaceArea),\n           'Argument 3 (surfaceArea) must be a number:' .. type(surfaceArea))\n    assert(isTable(aPosition),\n           'Argument 4 (aPosition) must be an array or vec3:' ..\n           type(aPosition))\n    assert(isTable(verticalAtPosition),\n           'Argument 5 (verticalAtPosition) must be an array or vec3:' ..\n           type(verticalAtPosition))\n    assert(isSNumber(altitudeAtPosition),\n           'Argument 6 (altitude) must be in meters:' ..\n           type(altitudeAtPosition))\n    assert(isSNumber(gravityAtPosition),\n           'Argument 7 (gravityAtPosition) must be number:' ..\n           type(gravityAtPosition))\n    local radius   = math.sqrt(surfaceArea/4/math.pi)\n    local distance = radius + altitudeAtPosition\n    local center   = vec3(aPosition) + distance*vec3(verticalAtPosition)\n    local GM       = gravityAtPosition * distance * distance\n    return mkBodyParameters(planetarySystemId, bodyId, radius, center, GM)\nend\n--\n-- isMapPosition - check for the presence of the 'MapPosition' fields\n-- valueToTest [in]: the value to be checked\n-- return: 'true' if all required fields are present in the input value\n--\nPlanetaryReference.isMapPosition  = isMapPosition\n-- PlanetaryReference INSTANCE METHODS:\n--\n-- getPlanetarySystem - get the planetary system using ID or MapPosition as key\n-- overload [in]: either the planetary system ID or a MapPosition that has it.\n-- return: instance of 'PlanetarySystem' class or nil on error\n--\nfunction PlanetaryReference:getPlanetarySystem(overload)\n    --if galaxyAtlas then\n        local planetarySystemId = overload\n        if isMapPosition(overload) then\n            planetarySystemId = overload.systemId\n        end\n        if type(planetarySystemId) == 'number' then\n            local system = self.galaxyAtlas[i]\n            if system then\n                if getmetatable(nv) ~= PlanetarySystem then\n                    system = mkPlanetarySystem(system)\n                end\n                return system\n            end\n        end\n    --end\n    --return nil\nend\n-- PlanetarySystem INSTANCE METHODS:\n--\n-- castIntersections - Find the closest body that intersects a \"ray cast\".\n-- origin [in]: the origin of the \"ray cast\" in world coordinates\n-- direction [in]: the direction of the \"ray cast\" as a 'vec3' instance.\n-- sizeCalculator [in]: (default: returns 1.05*radius) Returns size given body.\n-- bodyIds[in]: (default: all IDs in system) check only the given IDs.\n-- return: The closest body that blocks the cast or 'nil' if none.\n--\nfunction PlanetarySystem:castIntersections(origin,\n                                           direction,\n                                           sizeCalculator,\n                                           bodyIds)\n    local sizeCalculator = sizeCalculator or \n                            function (body) return 1.05*body.radius end\n    local candidates = {}\n    if bodyIds then\n        for _,i in ipairs(bodyIds) do candidates[i] = self[i] end\n    else\n        bodyIds = {}\n        for k,body in pairs(self) do\n            table.insert(bodyIds, k)\n            candidates[k] = body\n        end\n    end\n    local function compare(b1,b2)\n        local v1 = candidates[b1].center - origin\n        local v2 = candidates[b2].center - origin\n        return v1:len() < v2:len()\n    end\n    table.sort(bodyIds, compare)\n    local dir = direction:normalize()\n    for i, id in ipairs(bodyIds) do\n        local body   = candidates[id]\n        local c_oV3  = body.center - origin\n        local radius = sizeCalculator(body)\n        local dot    = c_oV3:dot(dir)\n        local desc   = dot^2 - (c_oV3:len2() - radius^2)\n        if desc >= 0 then\n            local root     = math.sqrt(desc)\n            local farSide  = dot + root\n            local nearSide = dot - root\n            if nearSide > 0 then\n                return body, farSide, nearSide\n            elseif farSide > 0 then\n                return body, farSide, nil\n            end\n        end\n    end\n    return nil, nil, nil\nend\n--\n-- closestBody - find the closest body to a given set of world coordinates\n-- coordinates       [in]: the world coordinates of position in space\n-- return: an instance of the BodyParameters object closest to 'coordinates'\n--\nfunction PlanetarySystem:closestBody(coordinates)\n    assert(type(coordinates) == 'table', 'Invalid coordinates.')\n    local minDistance2, body\n    local coord = vec3(coordinates)\n    for _,params in pairs(self) do\n        local distance2 = (params.center - coord):len2()\n        if not body or distance2 < minDistance2 then\n            body         = params\n            minDistance2 = distance2\n        end\n    end\n    return body\nend\n--\n-- convertToBodyIdAndWorldCoordinates - map to body Id and world coordinates\n-- overload [in]: an instance of MapPosition or a position string ('::pos...)\n-- return: a vec3 instance containing the world coordinates or 'nil' on error.\n--\nfunction PlanetarySystem:convertToBodyIdAndWorldCoordinates(overload)\n    local mapPosition = overload\n    if isString(overload) then\n        mapPosition = mkMapPosition(overload)\n    end\n    if mapPosition.bodyId == 0 then\n        return 0, vec3(mapPosition.latitude,\n                       mapPosition.longitude,\n                       mapPosition.altitude)\n    end\n    local params = self:getBodyParameters(mapPosition)\n    if params then\n        return mapPosition.bodyId,\n               params:convertToWorldCoordinates(mapPosition)\n    end\nend\n--\n-- getBodyParameters - get or create an instance of BodyParameters class\n-- overload [in]: either an instance of MapPosition or a body's ID.\n-- return: a BodyParameters instance or 'nil' if body ID is not found.\n--\nfunction PlanetarySystem:getBodyParameters(overload)\n    local bodyId = overload\n    if isMapPosition(overload) then\n        bodyId = overload.bodyId\n    end\n    assert(isSNumber(bodyId),\n               'Argument 1 (bodyId) must be a number:' .. type(bodyId))\n    return self[bodyId]\nend\n--\n-- getPlanetarySystemId - get the planetary system ID for this instance\n-- return: the planetary system ID or nil if no planets are in the system.\n--\nfunction PlanetarySystem:getPlanetarySystemId()\n    local k, v = next(self)\n    return v and v.planetarySystemId\nend\n-- BodyParameters INSTANCE METHODS:\n--\n-- convertToMapPosition - create an instance of MapPosition from coordinates\n-- worldCoordinates [in]: the world coordinates of the map position.\n-- return: an instance of MapPosition class\n--\nfunction BodyParameters:convertToMapPosition(worldCoordinates)\n    assert(isTable(worldCoordinates),\n           'Argument 1 (worldCoordinates) must be an array or vec3:' ..\n           type(worldCoordinates))\n    local worldVec  = vec3(worldCoordinates) \n    if self.bodyId == 0 then\n        return setmetatable({latitude  = worldVec.x,\n                             longitude = worldVec.y,\n                             altitude  = worldVec.z,\n                             bodyId    = 0,\n                             systemId  = self.planetarySystemId}, MapPosition)\n    end\n    local coords    = worldVec - self.center\n    local distance  = coords:len()\n    local altitude  = distance - self.radius\n    local latitude  = 0\n    local longitude = 0\n    if not float_eq(distance, 0) then\n        local phi = math.atan(coords.y, coords.x)\n        longitude = phi >= 0 and phi or (2*math.pi + phi)\n        latitude  = math.pi/2 - math.acos(coords.z/distance)\n    end\n    return setmetatable({latitude  = latitude,\n                         longitude = longitude,\n                         altitude  = altitude,\n                         bodyId    = self.bodyId,\n                         systemId  = self.planetarySystemId}, MapPosition)\nend\n--\n-- convertToWorldCoordinates - convert a map position to world coordinates\n-- overload [in]: an instance of MapPosition or a position string ('::pos...')\n--\nfunction BodyParameters:convertToWorldCoordinates(overload)\n    local mapPosition = isString(overload) and\n                                           mkMapPosition(overload) or overload\n    if mapPosition.bodyId == 0 then -- support deep space map position\n        return vec3(mapPosition.latitude,\n                    mapPosition.longitude,\n                    mapPosition.altitude)\n    end\n    assert(isMapPosition(mapPosition),\n           'Argument 1 (mapPosition) is not an instance of \"MapPosition\".')\n    assert(mapPosition.systemId == self.planetarySystemId,\n           'Argument 1 (mapPosition) has a different planetary system ID.')\n    assert(mapPosition.bodyId == self.bodyId,\n           'Argument 1 (mapPosition) has a different planetary body ID.')\n    local xproj = math.cos(mapPosition.latitude)\n    return self.center + (self.radius + mapPosition.altitude) *\n           vec3(xproj*math.cos(mapPosition.longitude),\n                xproj*math.sin(mapPosition.longitude),\n                math.sin(mapPosition.latitude))\nend\n--\n-- getAltitude - calculate the altitude of a point given in world coordinates.\n-- worldCoordinates [in]: the world coordinates of the point.\n-- return: the altitude in meters\n--\nfunction BodyParameters:getAltitude(worldCoordinates)\n    return (vec3(worldCoordinates) - self.center):len() - self.radius\nend\n--\n-- getDistance - calculate the distance to a point given in world coordinates.\n-- worldCoordinates [in]: the world coordinates of the point.\n-- return: the distance in meters\n--\nfunction BodyParameters:getDistance(worldCoordinates)\n    return (vec3(worldCoordinates) - self.center):len()\nend\n--\n-- getGravity - calculate the gravity vector induced by the body.\n-- worldCoordinates [in]: the world coordinates of the point.\n-- return: the gravity vector in meter/seconds^2\n--\nfunction BodyParameters:getGravity(worldCoordinates)\n    local radial = self.center - vec3(worldCoordinates) -- directed towards body\n    local len2   = radial:len2()\n    return (self.GM/len2) * radial/math.sqrt(len2)\nend\n-- end of module\nreturn setmetatable(PlanetaryReference,\n                    { __call = function(_,...)\n                                    return mkPlanetaryReference(...)\n                               end })\nend\nfunction Keplers()\n    --[[ \n  Provides methods for computing orbital information for an object\n  Usage:\n  Kepler = require('autoconf.custom.kepler')\n  alioth = Kepler({ GM=157470826617,\n                    bodyId=2,\n                    center={x=-8.000,y=-8.000,z=-126303.000},\n                    name='Alioth',\n                    planetarySystemId=0,\n                    radius=126068\n                  })\n  altitude = 6000\n  position = '::pos{0,2,0,0,6000}'\n  e, o     = alioth:escapeAndOrbitalSpeed(altitude)\n  orbit    = alioth:orbitalParameters(position, {0, o+1, 0})\n  print(\"Eccentricity \" .. orbit.eccentricity)\n  print(\"Perihelion \" .. orbit.periapsis.altitude)\n  print(\"Max. speed \" .. orbit.periapsis.speed)\n  print(\"Circular orbit speed \" .. orbit.periapsis.circularOrbitSpeed)\n  print(\"Aphelion \"  .. orbit.apoapsis.altitude)\n  print(\"Min. speed \" .. orbit.apoapsis.speed)\n  print(\"Orbital period \" .. orbit.period)\n  --- output:\n    Eccentricity 0.0018324307017878\n    Perihelion 6000.0\n    Max. speed 1092.9462297033\n    Circular orbit speed 1091.9462297033\n    Aphelion 6484.8994605062\n    Min. speed 1088.9480596194\n    Orbital period 762.02818214049\n  Methods:\n    Kepler:escapeAndOrbitalSpeed - for a given celestial body and altitude.\n    Kepler:orbitalParameters - for a given massless object and a celestial body.\n  Description\n  The motion of an object in the vicinity of substantially larger mass is\n  in the domain of the \"2-body problem\". By assuming the object whose motion\n  is of interest is of negligable mass simplifies the calculations of:\n  the speed to escape the body, the speed of a circular orbit, and the\n  parameters defining the orbit of the object (or the lack of orbit as the\n  case may be).\n  Orbital Parameters:\n     periapsis - the closest approach to the planet\n      apoapsis - the furthest point from the planet if in orbit (otherwise nil)\n  eccentricity - 0 for circular orbits\n                <1 for elliptical orbits\n                 1 for parabiolic trajectory\n                >1 for hyperbolic trajectory\n        period - time (in seconds) to complete an orbit\n  Also See: planetref.lua\n]]--\nlocal vec3       = require('cpml.vec3')\nlocal PlanetRef  = PlanetRef()\nlocal function isString(s) return type(s)   == 'string' end\nlocal function isTable(t)  return type(t)   == 'table'  end\nlocal function float_eq(a,b)\n    if a == 0 then return math.abs(b) < 1e-09 end\n    if b == 0 then return math.abs(a) < 1e-09 end\n    return math.abs(a - b) < math.max(math.abs(a),math.abs(b))*epsilon\nend\nKepler = {}\nKepler.__index = Kepler\n--\n-- escapeAndOrbitalSpeed - speed required to escape and for a circular orbit\n-- altitude [in]: the height of the orbit in meters above \"sea-level\"\n-- return: the speed in m/s needed to escape the celestial body and to orbit it.\n--\nfunction Kepler:escapeAndOrbitalSpeed(altitude)\n    assert(self.body)\n    -- P = -GMm/r and KE = mv^2/2 (no lorentz factor used)\n    -- mv^2/2 = GMm/r\n    -- v^2 = 2GM/r\n    -- v = sqrt(2GM/r1)\n    local distance = altitude + self.body.radius\n    if not float_eq(distance, 0) then\n        local orbit = math.sqrt(self.body.GM/distance)\n        return math.sqrt(2)*orbit, orbit\n    end\n    return nil, nil\nend\n--\n-- orbitalParameters: determine the orbital elements for a two-body system.\n-- overload [in]: the world coordinates or map coordinates of a massless object.\n-- velocity [in]: The velocity of the massless point object in m/s.\n-- return: the 6 orbital elements for the massless object.\n--\nfunction Kepler:orbitalParameters(overload, velocity)\n    assert(self.body)\n    assert(isTable(overload) or isString(overload))\n    assert(isTable(velocity))\n    local pos = (isString(overload) or PlanetRef.isMapPosition(overload)) and\n                            self.body:convertToWorldCoordinates(overload) or\n                vec3(overload)\n    local v   = vec3(velocity)\n    local r   = pos - self.body.center\n    local v2  = v:len2()\n    local d   = r:len()\n    local mu  = self.body.GM\n    local e   = ((v2 - mu/d)*r - r:dot(v)*v)/mu\n    local a   = mu/(2*mu/d - v2)\n    local ecc = e:len()\n    local dir = e:normalize()\n    local pd  = a*(1-ecc)\n    local ad  = a*(1+ecc)\n    local per = pd*dir + self.body.center\n    local apo = ecc <= 1 and -ad*dir + self.body.center or nil\n    local trm = math.sqrt(a*mu*(1-ecc*ecc))        \n    local Period = apo and 2*math.pi*math.sqrt(a^3/mu)\n    -- These are great and all, but, I need more.\n    local trueAnomaly = math.acos((e:dot(r))/(ecc*d))\n    if r:dot(v) < 0 then\n        trueAnomaly = -(trueAnomaly - 2*math.pi)\n    end        \n    -- Apparently... cos(EccentricAnomaly) = (cos(trueAnomaly) + eccentricity)/(1 + eccentricity * cos(trueAnomaly))\n    local EccentricAnomaly = math.acos((math.cos(trueAnomaly) + ecc)/(1 + ecc * math.cos(trueAnomaly)))\n    -- Then.... apparently if this is below 0, we should add 2pi to it\n    -- I think also if it's below 0, we're past the apoapsis?\n    local timeTau = EccentricAnomaly\n    if timeTau < 0 then\n        timeTau = timeTau + 2*math.pi\n    end\n    -- So... time since periapsis...\n    -- Is apparently easy if you get mean anomly.  t = M/n where n is mean motion, = 2*pi/Period\n    \n    \n    local MeanAnomaly = timeTau - ecc * math.sin(timeTau)\n    local TimeSincePeriapsis = MeanAnomaly/(2*math.pi/Period)\n    --system.print(MeanAnomaly .. \" - \" .. TimeSincePeriapsis .. \" - \" .. Period .. \" - \" .. EccentricAnomaly .. \" - \" .. timeTau .. \" - \" .. trueAnomaly)\n    -- Mean anom is 0 at periapsis, positive before it... and positive after it.\n    -- I guess this is why I needed to use timeTau and not EccentricAnomaly here\n    \n    local TimeToPeriapsis = Period - TimeSincePeriapsis\n    local TimeToApoapsis = TimeToPeriapsis + Period/2\n    if trueAnomaly - math.pi > 0 then -- TBH I think something's wrong in my formulas because I needed this.\n        TimeToPeriapsis = TimeSincePeriapsis\n        TimeToApoapsis = TimeToPeriapsis + Period/2\n    end\n    if TimeToApoapsis > Period then\n        TimeToApoapsis = TimeToApoapsis - Period\n    end\n    return { periapsis       = { position           = per,\n                                 speed              = trm/pd,\n                                 circularOrbitSpeed = math.sqrt(mu/pd),\n                                 altitude           = pd - self.body.radius},\n             apoapsis        = apo and\n                               { position           = apo,\n                                 speed              = trm/ad,\n                                 circularOrbitSpeed = math.sqrt(mu/ad),\n                                 altitude           = ad - self.body.radius},\n             currentVelocity = v,\n             currentPosition = pos,\n             eccentricity    = ecc,\n             period          = Period,\n             eccentricAnomaly = EccentricAnomaly,\n             meanAnomaly = MeanAnomaly,\n             timeToPeriapsis = TimeToPeriapsis,\n             timeToApoapsis = TimeToApoapsis\n           }\nend\n\nlocal function new(bodyParameters)\n    local params = PlanetRef.BodyParameters(bodyParameters.planetarySystemId,\n                                            bodyParameters.bodyId,\n                                            bodyParameters.radius,\n                                            bodyParameters.center,\n                                            bodyParameters.GM)\n    return setmetatable({body = params}, Kepler)\nend\nreturn setmetatable(Kepler, { __call = function(_,...) return new(...) end })\nend\nfunction Kinematics()\n    --[[ \n  DualUniverse kinematic equations\n  Author: JayleBreak\n  Usage (unit.start):\n  Kinematics = require('autoconf.custom.kinematics')\n  Methods:\n   computeAccelerationTime - \"relativistic\" version of t = (vf - vi)/a\n   computeDistanceAndTime - Return distance & time needed to reach final speed.\n   computeTravelTime - \"relativistic\" version of t=(sqrt(2ad+v^2)-v)/a\n  Description\n  DualUniverse increases the effective mass of constructs as their absolute\n  speed increases by using the \"lorentz\" factor (from relativity) as the scale\n  factor.  This results in an upper bound on the absolute speed of constructs\n  (excluding \"warp\" drive) that is set to 30 000 KPH (8 333 MPS). This module\n  provides utilities for computing some physical quantities taking this\n  scaling into account.\n]]--\nlocal Kinematic = {} -- just a namespace\nlocal C       = 30000000/3600\nlocal C2      = C*C\nlocal ITERATIONS = 100 -- iterations over engine \"warm-up\" period\nlocal function lorentz(v) return 1/math.sqrt(1 - v*v/C2) end\n--\n-- computeAccelerationTime - \"relativistic\" version of t = (vf - vi)/a\n-- initial      [in]: initial (positive) speed in meters per second.\n-- acceleration [in]: constant acceleration until 'finalSpeed' is reached.\n-- final        [in]: the speed at the end of the time interval.\n-- return: the time in seconds spent in traversing the distance\n--\nfunction Kinematic.computeAccelerationTime(initial, acceleration, final)\n    -- The low speed limit of following is: t=(vf-vi)/a (from: vf=vi+at)\n    local k1 = C*math.asin(initial/C)\n    return (C * math.asin(final/C) - k1)/acceleration\nend\n--\n-- computeDistanceAndTime - Return distance & time needed to reach final speed.\n-- initial[in]:     Initial speed in meters per second.\n-- final[in]:       Final speed in meters per second.\n-- restMass[in]:    Mass of the construct at rest in Kg.\n-- thrust[in]:      Engine's maximum thrust in Newtons.\n-- t50[in]:         (default: 0) Time interval to reach 50% thrust in seconds.\n-- brakeThrust[in]: (default: 0) Constant thrust term when braking.\n-- return: Distance (in meters), time (in seconds) required for change.\n--\nfunction Kinematic.computeDistanceAndTime(initial,\n                                          final,\n                                          restMass,\n                                          thrust,\n                                          t50,\n                                          brakeThrust)\n    -- This function assumes that the applied thrust is colinear with the\n    -- velocity. Furthermore, it does not take into account the influence\n    -- of gravity, not just in terms of its impact on velocity, but also\n    -- its impact on the orientation of thrust relative to velocity.\n    -- These factors will introduce (usually) small errors which grow as\n    -- the length of the trip increases.\n    t50            = t50 or 0\n    brakeThrust    = brakeThrust or 0 -- usually zero when accelerating\n    local tau0     = lorentz(initial)\n    local speedUp  = initial <= final\n    local a0       = thrust * (speedUp and 1 or -1)/restMass\n    local b0       = -brakeThrust/restMass\n    local totA     = a0+b0\n    if speedUp and totA <= 0 or not speedUp and totA >= 0 then\n        return -1, -1 -- no solution\n    end\n    local distanceToMax, timeToMax = 0, 0\n    -- If, the T50 time is set, then assume engine is at zero thrust and will\n    -- reach full thrust in 2*T50 seconds. Thrust curve is given by:\n    -- Thrust: F(z)=(a0*(1+sin(z))+2*b0)/2 where z=pi*(t/t50 - 1)/2\n    -- Acceleration is given by F(z)/m(z) where m(z) = m/sqrt(1-v^2/c^2)\n    -- or v(z)' = (a0*(1+sin(z))+2*b0)*sqrt(1-v(z)^2/c^2)/2\n    if a0 ~= 0 and t50 > 0 then\n        -- Closed form solution for velocity exists:\n        -- v(t) = -c*tan(w)/sqrt(tan(w)^2+1) => w = -asin(v/c)\n        -- w=(pi*t*(a0/2+b0)-a0*t50*sin(pi*t/2/t50)+*pi*c*k1)/pi/c\n        -- @ t=0, v(0) = vi\n        -- pi*c*k1/pi/c = -asin(vi/c)\n        -- k1 = asin(vi/c)\n        local k1  = math.asin(initial/C)\n        local c1  = math.pi*(a0/2+b0)\n        local c2  = a0*t50\n        local c3  = C*math.pi\n        local v = function(t)\n            local w  = (c1*t - c2*math.sin(math.pi*t/2/t50) + c3*k1)/c3\n            local tan = math.tan(w)\n            return C*tan/math.sqrt(tan*tan+1)\n        end\n        local speedchk = speedUp and function(s) return s >= final end or\n                                     function(s) return s <= final end\n        timeToMax  = 2*t50\n        if speedchk(v(timeToMax)) then\n            local lasttime = 0\n            while math.abs(timeToMax - lasttime) > 0.5 do\n                local t = (timeToMax + lasttime)/2\n                if speedchk(v(t)) then\n                    timeToMax = t \n                else\n                    lasttime = t\n                end\n            end\n        end\n        -- There is no closed form solution for distance in this case.\n        -- Numerically integrate for time t=0 to t=2*T50 (or less)\n        local lastv = initial\n        local tinc  = timeToMax/ITERATIONS\n        for step = 1, ITERATIONS do\n            local speed = v(step*tinc)\n            distanceToMax = distanceToMax + (speed+lastv)*tinc/2\n            lastv = speed\n        end\n        if timeToMax < 2*t50 then\n            return distanceToMax, timeToMax\n        end\n        initial     = lastv\n    end\n    -- At full thrust, acceleration only depends on the Lorentz factor:\n    -- v(t)' = (F/m(v)) = a*sqrt(1-v(t)^2/c^2) where a = a0+b0\n    -- -> v = c*sin((at+k1)/c)\n    -- @ t=0, v=vi: k1 = c*asin(vi/c)\n    -- -> t = (c*asin(v/c) - k1)/a\n    -- x(t)' = c*sin((at+k1)/c)\n    -- x = k2 - c^2 cos((at+k1)/c)/a\n    -- @ t=0, x=0: k2 = c^2 * cos(k1/c)/a\n    local k1       = C*math.asin(initial/C)\n    local time     = (C * math.asin(final/C) - k1)/totA\n    local k2       = C2 *math.cos(k1/C)/totA\n    local distance = k2 - C2 * math.cos((totA*time + k1)/C)/totA\n    return distance+distanceToMax, time+timeToMax\nend\n--\n-- computeTravelTime - \"relativistic\" version of t=(sqrt(2ad+v^2)-v)/a\n-- initialSpeed [in]: initial (positive) speed in meters per second\n-- acceleration [in]: constant acceleration until 'distance' is traversed\n-- distance [in]: the distance traveled in meters\n-- return: the time in seconds spent in traversing the distance\n--\nfunction Kinematic.computeTravelTime(initial, acceleration, distance)\n    -- The low speed limit of following is: t=(sqrt(2ad+v^2)-v)/a\n    -- (from: d=vt+at^2/2)\n    if distance == 0 then return 0 end\n    if acceleration > 0 then\n        local k1       = C*math.asin(initial/C)\n        local k2       = C2*math.cos(k1/C)/acceleration\n        return (C*math.acos(acceleration*(k2 - distance)/C2) - k1)/acceleration\n    end\n    assert(initial > 0, 'Acceleration and initial speed are both zero.')\n    return distance/initial\nend\nfunction Kinematic.lorentz(v) return lorentz(v) end\nreturn Kinematic\nend\nPlanetaryReference = PlanetRef()\ngalaxyReference = PlanetaryReference(Atlas())\nKinematic = Kinematics()\nKep = Keplers()\nfunction getDistanceDisplayString(distance)\n    local su = distance > 100000\n    local result = \"\"\n    if su then\n        -- Convert to SU\n        result = round(distance/1000/200,1) .. \" SU\"\n    else\n        -- Convert to KM\n        result = round(distance/1000,1) .. \" KM\"\n    end\n\n    return result\nend\n\nPlanetaryReference = PlanetRef()\ngalaxyReference = PlanetaryReference(Atlas())\n\nMapScreenButtons = {}\nMapScreenMouseX = 0\nMapScreenMouseY = 0\nMapScreenMouseDown = false\nMapScreenButtonSelected = 0\nlocal worldPos = vec3(core.getConstructWorldPos())\nlocal locX = (worldPos.x/400000)\nlocal locY = (worldPos.y/400000)*(-1)\nlocal destX = 0\nlocal destY = 0\nlocal sudistance = 0\nlocal loc = vec3(core.getConstructWorldPos())\nlocal ion = galaxyReference[0][120]  ---uses Atlas functions\nlocal thades = vec3(29165536.000, 10865536.000, 65536.000)\nlocal sinnen = vec3(58665536.000, 29665536.000, 58165536.000)\nlocal alioth = galaxyReference[0][2]    ---uses Atlas functions\nlocal madis = vec3(17465536.000, 22665536.000, -34464.000)\nlocal jago = vec3(-94134464.000, 12765536.000, -3634464.000)\nlocal symeon = vec3(14165536.000, -85634464.000, -934464.000)\nlocal lacobus = vec3(98865536.000, -13534464.000, -934464.000)\nlocal teoma = vec3(80865536.000, 54665536.000, -934464.000)\nlocal feli = vec3(-43534464.000, 22565536.000, -48934464.000)\nlocal talemai = vec3(-13234464.000, 55765536.000, 465536.000)\nlocal sicari = vec3(52765536.000, 27165536.000, 52065536.000)\nlocal distion = math.floor(ion:getDistance(loc)/200000)   ---uses getDistance functions----\nlocal distthades = string.format(\"%.2f\", math.sqrt((loc.x-thades.x)^2+(loc.y-thades.y)^2+(loc.z-thades.z)^2)/200000)\nlocal distalioth = math.floor(alioth:getDistance(loc)/200000)   ---uses getDistance functions----\nlocal distmadis = string.format(\"%.2f\", math.sqrt((loc.x-madis.x)^2+(loc.y-madis.y)^2+(loc.z-madis.z)^2)/200000)\nlocal distjago = string.format(\"%.2f\", math.sqrt((loc.x-jago.x)^2+(loc.y-jago.y)^2+(loc.z-jago.z)^2)/200000)\nlocal distlacobus = string.format(\"%.2f\", math.sqrt((loc.x-lacobus.x)^2+(loc.y-lacobus.y)^2+(loc.z-lacobus.z)^2)/200000)\nlocal distteoma = string.format(\"%.2f\", math.sqrt((loc.x-teoma.x)^2+(loc.y-teoma.y)^2+(loc.z-teoma.z)^2)/200000)\nlocal distsymeon = string.format(\"%.2f\", math.sqrt((loc.x-symeon.x)^2+(loc.y-symeon.y)^2+(loc.z-symeon.z)^2)/200000)\nlocal distfeli = string.format(\"%.2f\", math.sqrt((loc.x-feli.x)^2+(loc.y-feli.y)^2+(loc.z-feli.z)^2)/200000)\nlocal distsinnen = string.format(\"%.2f\", math.sqrt((loc.x-sinnen.x)^2+(loc.y-sinnen.y)^2+(loc.z-sinnen.z)^2)/200000)\nlocal disttalemai = string.format(\"%.2f\", math.sqrt((loc.x-talemai.x)^2+(loc.y-talemai.y)^2+(loc.z-talemai.z)^2)/200000)\nlocal distsicari = string.format(\"%.2f\", math.sqrt((loc.x-sicari.x)^2+(loc.y-sicari.y)^2+(loc.z-sicari.z)^2)/200000)\n\n\n\n      for i = 1,1 do\n   local button = {id = (\"b\"..1), enabled=true, td=\"<td>\", top=2/100, bottom=13/100, left=1/100, right=28/100}\n    table.insert(MapScreenButtons, button)\nend\n  for i = 2,2 do\n   local button = {id = (\"b\"..2), enabled=true, td=\"<td>\", top=15/100, bottom=26/100, left=1/100, right=30/100}\n    table.insert(MapScreenButtons, button)\nend\n    for i = 3,3 do\n    local button = {id = (\"b\"..3), enabled=true, td=\"<td>\", top=27/100, bottom=38/100, left=1/100, right=28/100}\n      table.insert(MapScreenButtons, button)\nend\n    for i = 4,4 do   \n    local button = {id = (\"b\"..4), enabled=true, td=\"<td>\", top=39/100, bottom=50/100, left=1/100, right=28/100}\n      table.insert(MapScreenButtons, button)\nend\n    for i = 5,5 do   \n    local button = {id = (\"b\"..5), enabled=true, td=\"<td>\", top=51/100, bottom=62/100, left=1/100, right=28/100}\n      table.insert(MapScreenButtons, button)\nend\n    for i = 6,6 do   \n    local button = {id = (\"b\"..6), enabled=true, td=\"<td>\", top=64/100, bottom=75/100, left=1/100, right=28/100}\n      table.insert(MapScreenButtons, button)\nend\n    for i = 7,7 do   \n    local button = {id = (\"b\"..7), enabled=true, td=\"<td>\", top=2/100, bottom=13/100, left=75/100, right=100/100}\n      table.insert(MapScreenButtons, button)\nend\n    for i = 8,8 do   \n    local button = {id = (\"b\"..8), enabled=true, td=\"<td>\", top=15/100, bottom=26/100, left=75/100, right=100/100}\n      table.insert(MapScreenButtons, button)\nend\n    for i = 9,9 do   \n    local button = {id = (\"b\"..9), enabled=true, td=\"<td>\", top=27/100, bottom=38/100, left=75/100, right=100/100}\n      table.insert(MapScreenButtons, button)\nend\n    for i = 10,10 do   \n    local button = {id = (\"b\"..10), enabled=true, td=\"<td>\", top=39/100, bottom=50/100, left=75/100, right=100/100}\n      table.insert(MapScreenButtons, button)\nend\n    for i = 11,11 do   \n    local button = {id = (\"b\"..11), enabled=true, td=\"<td>\", top=51/100, bottom=62/100, left=75/100, right=100/100}\n      table.insert(MapScreenButtons, button)\nend\n    for i = 12,12 do   \n    local button = {id = (\"b\"..12), enabled=true, td=\"<td>\", top=64/100, bottom=75/100, left=75/100, right=100/100}\n      table.insert(MapScreenButtons, button)\nend\n    for i = 13,13 do   \n    local button = {id = (\"b\"..13), enabled=true, td=\"<td>\", top=90/100, bottom=100/100, left=1/100, right=18/100}\n    table.insert(MapScreenButtons, button)\nend\nfunction evaluateButtons()\n  local selected = 0\n  \n  if #MapScreenButtons >= 1 then\n -- Set button styles\n       for i, button in ipairs(MapScreenButtons) do\n            if button.left < MapScreenMouseX and MapScreenMouseX < button.right and button.top < MapScreenMouseY and MapScreenMouseY < button.bottom then\n                if MapScreenMouseDown and MapScreenButtonSelected == i then\n                end\n                selected = i\n            end\n            if not button.enabled then\n            end\n\n        end\n  end\n  return selected\nend\n\nfunction onButtonDown(buttonNo)\n  local button = MapScreenButtons[buttonNo]    \n  if not button or not button.enabled then\n\treturn\n  end\nend\nfunction onButtonUp(buttonNo)\n  local button = MapScreenButtons[buttonNo]    \n  if not button or not button.enabled then\n    return\n  end\nfunction onClick(buttonNo)\n  local button = MapScreenButtons[buttonNo]    \n  if not button or not button.enabled then\n    return\n  end\nend\n  if buttonNo == 1 then\ndestX = 0\ndestY = 0\nsudistance = distalioth\n  elseif buttonNo == 2 then\ndestX = 43\ndestY = -56\nsudistance = distmadis\n  elseif buttonNo == 3 then\ndestX = 73\ndestY = -27\nsudistance = distthades        \n  elseif buttonNo == 4 then\ndestX = -33\ndestY = -139\nsudistance = disttalemai\n  elseif buttonNo == 5 then\ndestX = -109\ndestY = -56\nsudistance = distfeli        \n  elseif buttonNo == 6 then\ndestX = 131\ndestY = -68\nsudistance = distsicari        \n  elseif buttonNo == 7 then\ndestX = 35\ndestY = 214\nsudistance = distsymeon\n  elseif buttonNo == 8 then\ndestX = 146\ndestY = -74\nsudistance = distsinnen        \n  elseif buttonNo == 9 then\ndestX = -235\ndestY = -32\nsudistance = distjago       \n  elseif buttonNo == 10 then\ndestX = 202\ndestY = -137\nsudistance = distteoma        \n  elseif buttonNo == 11 then\ndestX = 7\ndestY = 247\nsudistance = distion        \n  elseif buttonNo == 12 then\ndestX = 247\ndestY = 34\nsudistance = distlacobus\n  elseif buttonNo == 13 then\n  unit.exit()\n  end\nend\n\nfunction updateScreen() \nwarpmath = math.floor(math.floor(core.getConstructMass()/ 1000)  * sudistance * 0.00025)\nhtml= ([[\n<svg class=\"bootstrap\" viewBox=\"0 0 1024 1024\" style=\"width:100%; height:100%\"><circle cx=\"500\" cy=\"500\" r=\"400\" stroke=\"darkgreen\" stroke-width=\"3\" transform=\"\"></circle><circle cx=\"500\" cy=\"500\" r=\"350\" stroke=\"darkgreen\" stroke-width=\"3\" transform=\"\" stroke-opacity=\"0.2\"></circle><circle cx=\"500\" cy=\"500\" r=\"300\" stroke=\"darkgreen\" stroke-width=\"3\" transform=\"\"></circle><circle cx=\"500\" cy=\"500\" r=\"250\" stroke=\"darkgreen\" stroke-width=\"3\" transform=\"\" stroke-opacity=\"0.2\"></circle><circle cx=\"500\" cy=\"500\" r=\"200\" stroke=\"darkgreen\" stroke-width=\"3\" transform=\"\"></circle><circle cx=\"500\" cy=\"500\" r=\"150\" stroke=\"darkgreen\" stroke-width=\"3\" transform=\"\" stroke-opacity=\"0.2\"></circle><circle cx=\"500\" cy=\"500\" r=\"100\" stroke=\"lightblue\" stroke-width=\"3\" transform=\"\"></circle><circle cx=\"500\" cy=\"500\" r=\"50\" stroke=\"lightblue\" stroke-width=\"3\" transform=\"\" stroke-opacity=\"0.2\"></circle><circle cx=\"500\" cy=\"500\" r=\"20\" stroke=\"Orange\" stroke-width=\"2\" transform=\"\"></circle><text x=\"510\" y=\"510\" fill=\"Yellow\">Helios</text><circle cx=\"-0.00\" cy=\"0\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"-0.00\" y=\"0\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Alioth</text><circle cx=\"7.16\" cy=\"247.59\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"7.16\" y=\"247.59\" transform=\"translate(480,480)\" fill=\"white\" font-size=\"20\">Ion</text><circle cx=\"35.41\" cy=\"214.09\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"35.41\" y=\"214.09\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Symeon</text><circle cx=\"-33.09\" cy=\"-139.41\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"-33.09\" y=\"-139.41\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Talemai</text><circle cx=\"202.16\" cy=\"-136.66\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"202.16\" y=\"-136.66\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Teoma</text><circle cx=\"247.16\" cy=\"33.84\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"247.16\" y=\"33.84\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Lacobus</text><circle cx=\"-108.84\" cy=\"-56.41\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"-108.84\" y=\"-56.41\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Feli</text><circle cx=\"72.91\" cy=\"-27.16\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"72.91\" y=\"-27.16\" transform=\"translate(500,485)\" fill=\"white\" font-size=\"20\">Thades</text><circle cx=\"43.66\" cy=\"-56.66\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"43.66\" y=\"-56.66\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Madis</text><circle cx=\"-235.34\" cy=\"-31.91\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"-235.34\" y=\"-31.91\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Jago</text><circle cx=\"131.91\" cy=\"-67.91\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"131.91\" y=\"-67.91\" transform=\"translate(475,480)\" fill=\"white\" font-size=\"20\">Sicari</text><circle cx=\"146.66\" cy=\"-74.16\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"146.66\" y=\"-74.16\" transform=\"translate(515,480)\" fill=\"white\" font-size=\"20\">Sinnen</text>\n<line stroke-linecap=\"undefined\" stroke-linejoin=\"undefined\" id=\"svg_1\" y2=\"]]..destY..[[\" x2=\"]]..destX..[[\" y1=\"]]..locY..[[\" x1=\"]]..locX..[[\" transform=\"translate(500,500)\" stroke-width=\"5\" stroke=\"#ff0000\" fill=\"none\"/>        \n<circle cx=\"]]..locX..[[\" cy=\"]]..locY..[[\" r=\"3\" stroke=\"black\" stroke-width=\"1\" fill=\"limegreen\" transform=\"translate(500,500)\"></circle>\n<text x=\"]]..locX..[[\" y=\"]]..locY..[[\" transform=\"translate(500,500)\" \nfill=\"limegreen\" font-size= \"4.5vh\" font-weight= \"bold\">//SHIP POSITION</text>\n</svg>\n<svg class=\"bootstrap\" viewBox=\"0 0 1024 612\" style=\"width:100%; height:100%\">\n <g>\n  <title>Layer 1</title>\n  <g id=\"svg_24\">\n   <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_8\" y=\"70\" x=\"55\" stroke-width=\"0\" fill=\"Yellow\">Alioth  :]]..distalioth..[[ SU</text>\n   <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_14\" y=\"170\" x=\"55\" stroke-width=\"0\" fill=\"Yellow\">Madis  :]]..distmadis..[[ SU</text>\n   <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_17\" y=\"270\" x=\"55\" stroke-width=\"0\" fill=\"Yellow\">Thades  :]]..distthades..[[ SU</text>\n   <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_20\" y=\"370\" x=\"55\" stroke-width=\"0\" fill=\"Yellow\">Talemai  :]]..disttalemai..[[ SU</text>\n   <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_23\" y=\"470\" x=\"55\" stroke-width=\"0\" fill=\"Yellow\">Feli  :]]..distfeli..[[ SU</text>\n   <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_26\" y=\"570\" x=\"55\" stroke-width=\"0\" fill=\"Yellow\">Sicari  :]]..distsicari..[[ SU</text>\n   <g id=\"svg_12\">\n    <rect rx=\"10\" id=\"svg_1\" height=\"50\" width=\"250\" y=\"30\" x=\"15\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n    <rect rx=\"10\" id=\"svg_3\" height=\"50\" width=\"250\" y=\"105\" x=\"15\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n    <rect rx=\"10\" id=\"svg_7\" height=\"50\" width=\"250\" y=\"180\" x=\"15\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n    <rect rx=\"10\" id=\"svg_9\" height=\"50\" width=\"250\" y=\"255\" x=\"15\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n    <rect rx=\"10\" id=\"svg_10\" height=\"50\" width=\"250\" y=\"330\" x=\"15\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n    <rect rx=\"10\" id=\"svg_11\" height=\"50\" width=\"250\" y=\"405\" x=\"15\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n   </g>\n  </g>\n  <g id=\"svg_40\">\n   <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_25\" y=\"70\" x=\"997.163642\" stroke-width=\"0\" fill=\"Yellow\">Symeon  :]]..distsymeon..[[ SU</text>\n   <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_27\" y=\"170\" x=\"997.163642\" stroke-width=\"0\" fill=\"Yellow\">Sinnen  :]]..distsinnen..[[ SU</text>\n   <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_28\" y=\"270\" x=\"997.163642\" stroke-width=\"0\" fill=\"Yellow\">Jago  :]]..distjago..[[ SU</text>\n   <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_30\" y=\"370\" x=\"997.163642\" stroke-width=\"0\" fill=\"Yellow\">Teoma  :]]..distteoma..[[ SU</text>\n   <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_31\" y=\"470\" x=\"997.163642\" stroke-width=\"0\" fill=\"Yellow\">Ion  :]]..distion..[[ SU</text>\n   <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_32\" y=\"570\" x=\"997.163642\" stroke-width=\"0\" fill=\"Yellow\">Lacobus  :]]..distlacobus..[[ SU</text>\n   <g id=\"svg_39\">\n    <rect rx=\"10\" id=\"svg_33\" height=\"50\" width=\"250\" y=\"30\" x=\"760\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n    <rect rx=\"10\" id=\"svg_34\" height=\"50\" width=\"250\" y=\"105\" x=\"760\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n    <rect rx=\"10\" id=\"svg_35\" height=\"50\" width=\"250\" y=\"180\" x=\"760\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n    <rect rx=\"10\" id=\"svg_36\" height=\"50\" width=\"250\" y=\"255\" x=\"760\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n    <rect rx=\"10\" id=\"svg_37\" height=\"50\" width=\"250\" y=\"330\" x=\"760\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n    <rect rx=\"10\" id=\"svg_38\" height=\"50\" width=\"250\" y=\"405\" x=\"760\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n   </g>\n  </g>\n </g>\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"30\" id=\"svg_32\" y=\"700\" x=\"20\" stroke-width=\"0\" fill=\"LightBlue\">Est. Warp Cost: ]]..warpmath..[[</text>\n  <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"30\" id=\"svg_32\" y=\"750\" x=\"20\" stroke-width=\"0\" fill=\"LightBlue\">Construct Weight: ]]..math.floor(core.getConstructMass()/ 1000)..[[ tons</text>\n    \n</svg>    \n    ]])\nscreen.setHTML(html)\nend\nunit.setTimer(\"spacemap\",.08)\n\n\n","filter":{"args":[],"signature":"start()","slotKey":"-1"},"key":"2"},{"code":"updateScreen()","filter":{"args":[{"value":"spacemap"}],"signature":"tick(timerId)","slotKey":"-1"},"key":"3"}],"methods":[],"events":[]}




 

 

Link to comment
Share on other sites

7 hours ago, Go0och said:

Can you explain how to set this up? I've tried copying script to board after linking to container and screen. Nothing working yet.

I do not see anywhere I said to hook to a container. I think you may have misread. Link screen then core

Link to comment
Share on other sites

Thanks for this, what you could do for calculating the distances would be to stick the planet data into a table and then use a for k,v in pairs.

 

function getPlanetData()
    local planets = {} 
    planets[1] = { ["name"] = "Alioth", x = -4.000, y = -4.000, z = -60534.000, grav = 9.891} 
    planets[2] = { ["name"] = "Ion", x = 2865536.000, y = -99034464.000, z = -934464.000, grav = 3.5325} 
    planets[3] = { ["name"] = "Symeon", x = 14165536.000, y = -85634464.000, z = -934464.000, grav = 3.8465} 
    planets[4] = { ["name"] = "Talemai", x = -13234464.000, y = 55765536.000, z = 465536.000, grav = 4.553} 
    planets[5] = { ["name"] = "Teoma", x = 80865536.000, y = 54665536.000, z = -934464.000, grav = 4.7885} 
    planets[6] = { ["name"] = "Lacobus", x = 98865536.000, y = -13534464.000, z = -934464.000, grav = 4.4745} 
    planets[7] = { ["name"] = "Feli", x = -43534464.000, y = 22565536.000, z = -48934464.000, grav = 4.71} 
    planets[8] = { ["name"] = "Thades", x = 29165536.000, y = 10865536.000, z = 65536.000, grav = 4.867} 
    planets[9] = { ["name"] = "Madis", x = 17465536.000, y = 22665536.000, z = -34464.000, grav = 3.5325} 
    planets[10] = { ["name"] = "Jago", x = -94134464.000, y = 12765536.000, z = -3634464.000, grav = 4.9455} 
    planets[11] = { ["name"] = "Sicari", x = 52765536.000, y = 27165536.000, z = 52065536.000, grav = 4.0035} 
    planets[12] = { ["name"] = "Sinnen", x = 58665536.000, y = 29665536.000, z = 58165536.000, grav = 4.3175}
    return planets
end
pos = core.getConstructWorldPos()
planet = getPlanetData()

for k,v in pairs(planet) do
	    dist = string.format("%.2f", math.sqrt((planet[k].x - pos[1])^2+(planet[k].y - pos[2])^2+(planet[k].z - pos[3])^2)/200000)
    	mapdata = mapdata .. [[
    		<tr><td>]]..planet[k].name..[[</td><td>]]..dist..[[</td></tr>
    	]]
end

 

Link to comment
Share on other sites

Hello.

 

I tryed to get this to work but no luck for me so i wonder what i did wrong. If somebody could explain it to me i would be thankfull.

 

So i did mount a programing board amd a screen to my ship. Opened the lua editor of the board and in the unit slot creaded a filter at start where i did put all of the  code. Did hit aplay and linked the screen and the core. Then i renamed thze slots as screen and core. Tryed to activate (no errors), turn on the screen (S size) and nothing. Im lost here. 

I have noticed that you have in the code a timer with the name spaceMap but i cant figure out what should be the code inside of it. 

 

Link to comment
Share on other sites

I have added a programming board and screen to a dynamic construct.  Named the screen and core slots, "screen" and "core" respectively.  Added a "start" event to the board's "unit" slot, and pasted the code into that window.  I activate the board and turn on the screen, and nothing displays on the screen.  I do not get any script errors either.  I have now tried with XS, S, and M screens, all with the same result.  Do these scripts require that the construct have a warp drive installed before they will function?

Link to comment
Share on other sites

looks awesome cant wait to try it out.

kinda gave me another idea for an information display, showing all info about the closest celestial object. Atmos Height and presence, ores in location, you know, useful stuff.

Link to comment
Share on other sites

I modified your Unit -> start() lua to include real time TTD(time to destination based on accel and velocity) if your flying through space and it takes a second monitor you can put in front of your chair. Also changes the colors and styling up a little bit. One issue is it doesn't work when the game is in 4k the mouse clicks dont register haven't fixed that yet thought. 

 

function Atlas()
        return {
    [0] = {
      [1]={
        GM=6930729684,
        bodyId=1,
        center={x=17465536.000,y=22665536.000,z=-34464.000},
        name='Madis',
        planetarySystemId=0,
        radius=44300
      },
      [2]={
        GM=157470826617,
        bodyId=2,
        center={x=-8.000,y=-8.000,z=-126303.000},
        name='Alioth',
        planetarySystemId=0,
        radius=126068
      },
      [3]={
        GM=11776905000,
        bodyId=3,
        center={x=29165536.000,y=10865536.000,z=65536.000},
        name='Thades',
        planetarySystemId=0,
        radius=49000
      },
      [4]={
        GM=14893847582,
        bodyId=4,
        center={x=-13234464.000,y=55765536.000,z=465536.000},
        name='Talemai',
        planetarySystemId=0,
        radius=57450
      },
      [5]={
        GM=16951680000,
        bodyId=5,
        center={x=-43534464.000,y=22565536.000,z=-48934464.000},
        name='Feli',
        planetarySystemId=0,
        radius=60000
      },
      [6]={
        GM=10502547741,
        bodyId=6,
        center={x=52765536.000,y=27165538.000,z=52065535.000},
        name='Sicari',
        planetarySystemId=0,
        radius=51100
      },
      [7]={
        GM=13033380591,
        bodyId=7,
        center={x=58665538.000,y=29665535.000,z=58165535.000},
        name='Sinnen',
        planetarySystemId=0,
        radius=54950
      },
      [8]={
        GM=18477723600,
        bodyId=8,
        center={x=80865538.000,y=54665536.000,z=-934463.940},
        name='Teoma',
        planetarySystemId=0,
        radius=62000
      },
      [9]={
        GM=18606274330,
        bodyId=9,
        center={x=-94134462.000,y=12765534.000,z=-3634464.000},
        name='Jago',
        planetarySystemId=0,
        radius=61590
      },
      [10]={
        GM=78480000,
        bodyId=10,
        center={x=17448118.224,y=22966846.286,z=143078.820},
        name='Madis Moon 1',
        planetarySystemId=0,
        radius=10000
      },
      [11]={
        GM=237402000,
        bodyId=11,
        center={x=17194626.000,y=22243633.880,z=-214962.810},
        name='Madis Moon 2',
        planetarySystemId=0,
        radius=11000
      },
      [12]={
        GM=265046609,
        bodyId=12,
        center={x=17520614.000,y=22184730.000,z=-309989.990},
        name='Madis Moon 3',
        planetarySystemId=0,
        radius=15005
      },
      [21]={
        GM=2118960000,
        bodyId=21,
        center={x=457933.000,y=-1509011.000,z=115524.000},
        name='Alioth Moon 1',
        planetarySystemId=0,
        radius=30000
      },
      [22]={
        GM=2165833514,
        bodyId=22,
        center={x=-1692694.000,y=729681.000,z=-411464.000},
        name='Alioth Moon 4',
        planetarySystemId=0,
        radius=30330
      },
      [26]={
        GM=68234043600,
        bodyId=26,
        center={x=-1404835.000,y=562655.000,z=-285074.000},
        name='Sanctuary',
        planetarySystemId=0,
        radius=83400
      },
      [30]={
        GM=211564034,
        bodyId=30,
        center={x=29214402.000,y=10907080.695,z=433858.200},
        name='Thades Moon 1',
        planetarySystemId=0,
        radius=14002
      },
      [31]={
        GM=264870000,
        bodyId=31,
        center={x=29404193.000,y=10432768.000,z=19554.131},
        name='Thades Moon 2',
        planetarySystemId=0,
        radius=15000
      },
      [40]={
        GM=141264000,
        bodyId=40,
        center={x=-13503090.000,y=55594325.000,z=769838.640},
        name='Talemai Moon 2',
        planetarySystemId=0,
        radius=12000
      },
      [41]={
        GM=106830900,
        bodyId=41,
        center={x=-12800515.000,y=55700259.000,z=325207.840},
        name='Talemai Moon 3',
        planetarySystemId=0,
        radius=11000
      },
      [42]={
        GM=264870000,
        bodyId=42,
        center={x=-13058408.000,y=55781856.000,z=740177.760},
        name='Talemai Moon 1',
        planetarySystemId=0,
        radius=15000
      },
      [50]={
        GM=499917600,
        bodyId=50,
        center={x=-43902841.780,y=22261034.700,z=-48862386.000},
        name='Feli Moon 1',
        planetarySystemId=0,
        radius=14000
      },
      [70]={
        GM=396912600,
        bodyId=70,
        center={x=58969616.000,y=29797945.000,z=57969449.000},
        name='Sinnen Moon 1',
        planetarySystemId=0,
        radius=17000
      },
      [100]={
        GM=13975172474,
        bodyId=100,
        center={x=98865536.000,y=-13534464.000,z=-934461.990},
        name='Lacobus',
        planetarySystemId=0,
        radius=55650
      },
      [101]={
        GM=264870000,
        bodyId=101,
        center={x=98905288.170,y=-13950921.100,z=-647589.530},
        name='Lacobus Moon 3',
        planetarySystemId=0,
        radius=15000
      },
      [102]={
        GM=444981600,
        bodyId=102,
        center={x=99180968.000,y=-13783862.000,z=-926156.400},
        name='Lacobus Moon 1',
        planetarySystemId=0,
        radius=18000
      },
      [103]={
        GM=211503600,
        bodyId=103,
        center={x=99250052.000,y=-13629215.000,z=-1059341.400},
        name='Lacobus Moon 2',
        planetarySystemId=0,
        radius=14000
      },
      [110]={
        GM=9204742375,
        bodyId=110,
        center={x=14165536.000,y=-85634465.000,z=-934464.300},
        name='Symeon',
        planetarySystemId=0,
        radius=49050
      },
      [120]={
        GM=7135606629,
        bodyId=120,
        center={x=2865536.700,y=-99034464.000,z=-934462.020},
        name='Ion',
        planetarySystemId=0,
        radius=44950
      },
      [121]={
        GM=106830900,
        bodyId=121,
        center={x=2472916.800,y=-99133747.000,z=-1133582.800},
        name='Ion Moon 1',
        planetarySystemId=0,
        radius=11000
      },
      [122]={
        GM=176580000,
        bodyId=122,
        center={x=2995424.500,y=-99275010.000,z=-1378480.700},
        name='Ion Moon 2',
        planetarySystemId=0,
        radius=15000
      }
     }
    }
    end
function PlanetRef()
--[[
  Provide coordinate transforms and access to kinematic related parameters
  Author: JayleBreak
  Usage (unit.start):
  PlanetaryReference = require('planetref')
  galaxyReference = PlanetaryReference(referenceTableSource)
  helios = galaxyReference[0] -- PlanetaryReference.PlanetarySystem instance
  alioth = helios[2]          -- PlanetaryReference.BodyParameters instance
  Methods:
    PlanetaryReference:getPlanetarySystem - based on planetary system ID.
    PlanetaryReference.isMapPosition - 'true' if an instance of 'MapPosition'
    PlanetaryReference.createBodyParameters - for entry into reference table
    PlanetaryReference.BodyParameters - a class containing a body's information.
    PlanetaryReference.MapPosition - a class for map coordinates
    PlanetaryReference.PlanetarySystem - a container for planetary system info.
    PlanetarySystem:castIntersections - from a position in a given direction.
    PlanetarySystem:closestBody - to the specified coordinates.
    PlanetarySystem:convertToBodyIdAndWorldCoordinates - from map coordinates.
    PlanetarySystem:getBodyParameters - from reference table.
    PlanetarySystem:getPlanetarySystemId - for the instance.
    BodyParameters:convertToWorldCoordinates - from map coordinates
    BodyParameters:convertToMapPosition - from world coordinates
    BodyParameters:getAltitude - of world coordinates
    BodyParameters:getDistance - from center to world coordinates
    BodyParameters:getGravity - at a given position in world coordinates.
  Description
  An instance of the 'PlanetaryReference' "class" can contain transform and
  kinematic reference information for all planetary systems in DualUniverse.
  Each planetary system is identified by a numeric identifier. Currently,
  the only planetary system, Helios, has the identifier: zero. This "class"
  supports the indexing ('[]') operation which is equivalent to the
  use of the 'getPlanetarySystem' method. It also supports the 'pairs()'
  method for iterating over planetary systems.

  An instance of the 'PlanetarySystem' "class" contains all reference
  information for a specific system. It supports the indexing ('[]') and
  'pairs()' functions which allows iteration over each "body" in the
  system where the key is the numeric body ID. It also supports the
  'tostring()' method.
  An instance of the 'BodyParameters' "class" contains all reference
  information for a single celestial "body" (a moon or planet). It supports
  the 'tostring()' method, and contains the data members:
          planetarySystemId - numeric planetary system ID
          bodyId            - numeric body ID
          radius            - radius of the body in meters (zero altitude)
          center            - world coordinates of the body's center position
          GM                - the gravitation parameter (g = GM/radius^2)
  Note that the user is allowed to add custom fields (e.g. body name), but
  should insure that complex table values have the '__tostring' metamethod
  implemented.
  Transform and Kinematics:
  "World" coordinates is a cartesian coordinate system with an origin at an
  arbitrary fixed point in a planetary system and with distances measured in
  meters. The coordinates are expressible either as a simple table of 3 values
  or an instance of the 'vec3' class.  In either case, the planetary system
  identity is implicit.
  "Map" coordinates is a geographic coordinate system with an origin at the
  center of an identified (by a numeric value) celestial body which is a
  member of an identified (also a numeric value) planetary system. Note that
  the convention that latitude, longitude, and altitude values will be the
  position's x, y, and z world coordinates in the special case of body ID 0.
  The kinematic parameters in the reference data permit calculations of the
  gravitational attraction of the celestial body on other objects.
  Reference Data:
  This is an example of reference data with a single entry assigned to
  planetary system ID 0, and body ID 2 ('Alioth'):
    referenceTable = {
          [0] = { [2] = { planetarySystemId = 0,
                          bodyId = 2,
                          radius = 126068,
                          center = vec3({x=-8, y=-8, z=-126303}),
                          GM = 1.572199+11 } -- as in F=-GMm/r^2
          }
      }
    ref=PlanetaryReference(referenceTable)
  Collecting Reference Data:
  A combination of information from the "Map" screen in the DU user interface,
  and values reported by the DU Lua API can be the source of the reference
  table's data (planetarySystemId, bodyId, and surfaceArea is from the user
  interface):
    referenceTable = {}
    referenceTable[planetarySystemId][bodyId] =
         PlanetaryReference.createBodyParameters(planetarySystemId,
                                                 bodyId,
                                                 surfaceArea,
                                                 core.getConstructWorldPos(),
                                                 core.getWorldVertical(),
                                                 core.getAltitude(),
                                                 core.g())
  Adapting Data Sources:
  Other sources of data can be adapted or converted. An example of adapting a
  table, defined in the file: 'planets.lua', containing information on a single
  planetary system and using celestial body name as the key follows (note that
  a 'name' field is added to the BodyParameters instance transparently after
  construction, and the '__pairs' meta function is required to support the
  'closestBody' and '__tostring' methods):
    ref=PlanetaryReference(
        {[0] = setmetatable(require('planets'),
                        { __index = function(bodies, bodyId)
                             for _,v in pairs(bodies) do
                                 if v and v.bodyId == bodyId then return v end
                             end
                             return nil
                           end,
                         __pairs = function(bodies)
                             return function(t, k)
                                     local nk, nv = next(t, k)
                                     if nv then
                                         local GM = nv.gravity * nv.radius^2
                                         local bp = BodyParameters(0,
                                                                   nv.id,
                                                                   nv.radius,
                                                                   nv.pos,
                                                                   GM)
                                         bp.name = nk
                                         return nk, bp
                                    end
                                    return nk, nv
                                 end, bodies, nil
                           end })
    })

  Converting Data Sources:
  An instance of 'PlanetaryReference' that has been adapted to a data source
  can be used to convert that source to simple table. For example,
  using the adapted instance shown above:
    load('convertedData=' .. tostring(ref))()
    newRef=PlanetaryReference(convertedData)
  Also See: kepler.lua
  ]]--
--[[                    START OF LOCAL IMPLEMENTATION DETAILS             ]]--
-- Type checks
local function isNumber(n)  return type(n)           == 'number' end
local function isSNumber(n) return type(tonumber(n)) == 'number' end
local function isTable(t)   return type(t)           == 'table'  end
local function isString(s)  return type(s)           == 'string' end
local function isVector(v)  return isTable(v)
                                    and isNumber(v.x and v.y and v.z) end
local function isMapPosition(m) return isTable(m) and isNumber(m.latitude  and
                                                               m.longitude and
                                                               m.altitude  and
                                                               m.bodyId    and
                                                               m.systemId) end
-- Constants
local deg2rad    = math.pi/180
local rad2deg    = 180/math.pi
local epsilon    = 1e-10
local num        = ' *([+-]?%d+%.?%d*e?[+-]?%d*)'
local posPattern = '::pos{' .. num .. ',' .. num .. ',' ..  num .. ',' ..
                   num ..  ',' .. num .. '}'
-- Utilities
local utils  = require('cpml.utils')
local vec3   = require('cpml.vec3')
local clamp  = utils.clamp
local function float_eq(a,b)
    if a == 0 then return math.abs(b) < 1e-09 end
    if b == 0 then return math.abs(a) < 1e-09 end
    return math.abs(a - b) < math.max(math.abs(a),math.abs(b))*epsilon
end
local function formatNumber(n)
    local result = string.gsub(
                    string.reverse(string.format('%.4f',n)),
                    '^0*%.?','')
    return result == '' and '0' or string.reverse(result)
end
local function formatValue(obj)
    if isVector(obj) then
        return string.format('{x=%.3f,y=%.3f,z=%.3f}', obj.x, obj.y, obj.z)
    end
    if isTable(obj) and not getmetatable(obj) then
        local list = {}
        local nxt  = next(obj)
        if type(nxt) == 'nil' or nxt == 1 then -- assume this is an array
            list = obj
        else
            for k,v in pairs(obj) do
                local value = formatValue(v)
                if type(k) == 'number' then
                    table.insert(list, string.format('[%s]=%s', k, value))
                else
                    table.insert(list, string.format('%s=%s',   k, value))
                end
            end
        end
        return string.format('{%s}', table.concat(list, ','))
    end
    if isString(obj) then
        return string.format("'%s'", obj:gsub("'",[[\']]))
    end
    return tostring(obj)
end
-- CLASSES
-- BodyParameters: Attributes of planetary bodies (planets and moons)
local BodyParameters = {}
BodyParameters.__index = BodyParameters
BodyParameters.__tostring =
    function(obj, indent)
        local sep = indent or ''
        local keys = {}
        for k in pairs(obj) do table.insert(keys, k) end
        table.sort(keys)
        local list = {}
        for _, k in ipairs(keys) do
            local value = formatValue(obj[k])
            if type(k) == 'number' then
                table.insert(list, string.format('[%s]=%s', k, value))
            else
                table.insert(list, string.format('%s=%s', k, value))
            end
        end
        if indent then
            return string.format('%s%s',
                                 indent,
                                 table.concat(list, ',\n' .. indent))
        end
        return string.format('{%s}', table.concat(list, ','))
    end
BodyParameters.__eq = function(lhs, rhs)
        return lhs.planetarySystemId == rhs.planetarySystemId and
               lhs.bodyId            == rhs.bodyId            and
               float_eq(lhs.radius, rhs.radius)               and
               float_eq(lhs.center.x, rhs.center.x)           and
               float_eq(lhs.center.y, rhs.center.y)           and
               float_eq(lhs.center.z, rhs.center.z)           and
               float_eq(lhs.GM, rhs.GM)
    end
local function mkBodyParameters(systemId, bodyId, radius, worldCoordinates, GM)
    -- 'worldCoordinates' can be either table or vec3
    assert(isSNumber(systemId),
           'Argument 1 (planetarySystemId) must be a number:' .. type(systemId))
    assert(isSNumber(bodyId),
           'Argument 2 (bodyId) must be a number:' .. type(bodyId))
    assert(isSNumber(radius),
           'Argument 3 (radius) must be a number:' .. type(radius))
    assert(isTable(worldCoordinates),
           'Argument 4 (worldCoordinates) must be a array or vec3.' ..
           type(worldCoordinates))
    assert(isSNumber(GM),
           'Argument 5 (GM) must be a number:' .. type(GM))
    return setmetatable({planetarySystemId = tonumber(systemId),
                         bodyId            = tonumber(bodyId),
                         radius            = tonumber(radius),
                         center            = vec3(worldCoordinates),
                         GM                = tonumber(GM) }, BodyParameters)
end
-- MapPosition: Geographical coordinates of a point on a planetary body.
local MapPosition = {}
MapPosition.__index = MapPosition
MapPosition.__tostring = function(p)
        return string.format('::pos{%d,%d,%s,%s,%s}',
                             p.systemId,
                             p.bodyId,
                             formatNumber(p.latitude*rad2deg),
                             formatNumber(p.longitude*rad2deg),
                             formatNumber(p.altitude))
    end
MapPosition.__eq       = function(lhs, rhs)
        return lhs.bodyId   == rhs.bodyId              and
               lhs.systemId == rhs.systemId            and
               float_eq(lhs.latitude,   rhs.latitude)  and
               float_eq(lhs.altitude,   rhs.altitude)  and
               (float_eq(lhs.longitude, rhs.longitude) or
                float_eq(lhs.latitude, math.pi/2)      or
                float_eq(lhs.latitude, -math.pi/2))
    end
-- latitude and longitude are in degrees while altitude is in meters
local function mkMapPosition(overload, bodyId, latitude, longitude, altitude)
    local systemId = overload -- Id or '::pos{...}' string
    if isString(overload) and not longitude and not altitude and
                              not bodyId    and not latitude then
        systemId, bodyId, latitude, longitude, altitude =
                                            string.match(overload, posPattern)
        assert(systemId, 'Argument 1 (position string) is malformed.')
    else
        assert(isSNumber(systemId),
               'Argument 1 (systemId) must be a number:' .. type(systemId))
        assert(isSNumber(bodyId),
               'Argument 2 (bodyId) must be a number:' .. type(bodyId))
        assert(isSNumber(latitude),
               'Argument 3 (latitude) must be in degrees:' .. type(latitude))
        assert(isSNumber(longitude),
               'Argument 4 (longitude) must be in degrees:' .. type(longitude))
        assert(isSNumber(altitude),
               'Argument 5 (altitude) must be in meters:' .. type(altitude))
    end
    systemId  = tonumber(systemId)
    bodyId    = tonumber(bodyId)
    latitude  = tonumber(latitude)
    longitude = tonumber(longitude)
    altitude  = tonumber(altitude)
    if bodyId == 0 then -- this is a hack to represent points in space
        return setmetatable({latitude  = latitude,
                             longitude = longitude,
                             altitude  = altitude,
                             bodyId    = bodyId,
                             systemId  = systemId}, MapPosition)
    end
    return setmetatable({latitude  = deg2rad*clamp(latitude, -90, 90),
                         longitude = deg2rad*(longitude % 360),
                         altitude  = altitude,
                         bodyId    = bodyId,
                         systemId  = systemId}, MapPosition)
end
-- PlanetarySystem - map body IDs to BodyParameters
local PlanetarySystem = {}
PlanetarySystem.__index = PlanetarySystem
PlanetarySystem.__tostring =
    function (obj, indent)
        local sep = indent and (indent .. '  ' )
        local bdylist = {}
        local keys = {}
        for k in pairs(obj) do table.insert(keys, k) end
        table.sort(keys)
        for _, bi in ipairs(keys) do
            bdy = obj[bi]
            local bdys = BodyParameters.__tostring(bdy, sep)
            if indent then
                table.insert(bdylist,
                             string.format('[%s]={\n%s\n%s}',
                                           bi, bdys, indent))
            else
                table.insert(bdylist, string.format('  [%s]=%s', bi, bdys))
            end
        end
        if indent then
            return string.format('\n%s%s%s',
                                 indent,
                                 table.concat(bdylist, ',\n' .. indent),
                                 indent)
        end
        return string.format('{\n%s\n}', table.concat(bdylist, ',\n'))
    end
local function mkPlanetarySystem(referenceTable)
    local atlas = {}
    local pid
    for _, v in pairs(referenceTable) do
        local id = v.planetarySystemId
        if type(id) ~= 'number' then
            error('Invalid planetary system ID: ' .. tostring(id))
        elseif pid and id ~= pid then
            error('Mismatch planetary system IDs: ' .. id .. ' and '
                  .. pid)
        end
        local bid = v.bodyId
        if type(bid) ~= 'number' then
            error('Invalid body ID: ' .. tostring(bid))
        elseif atlas[bid] then
            error('Duplicate body ID: ' .. tostring(bid))
        end
        setmetatable(v.center, getmetatable(vec3.unit_x))
        atlas[bid] = setmetatable(v, BodyParameters)
        pid = id
    end
    return setmetatable(atlas, PlanetarySystem)
end
-- PlanetaryReference - map planetary system ID to PlanetarySystem
PlanetaryReference = {}
local function mkPlanetaryReference(referenceTable)
    return setmetatable({ galaxyAtlas = referenceTable or {} },
                          PlanetaryReference)
end
PlanetaryReference.__index        =
    function(t,i)
        if type(i) == 'number' then
            local system = t.galaxyAtlas[i]
            return mkPlanetarySystem(system)
        end
        return rawget(PlanetaryReference, i)
    end
PlanetaryReference.__pairs        =
    function(obj)
        return  function(t, k)
                    local nk, nv = next(t, k)
                    return nk, nv and mkPlanetarySystem(nv)
                end, obj.galaxyAtlas, nil
    end
PlanetaryReference.__tostring     =
    function (obj)
        local pslist = {}
        for _,ps in pairs(obj or {}) do
            local psi = ps:getPlanetarySystemId()
            local pss = PlanetarySystem.__tostring(ps, '    ')
            table.insert(pslist,
                         string.format('  [%s]={%s\n  }', psi, pss))
        end
        return string.format('{\n%s\n}\n', table.concat(pslist,',\n'))
    end
--[[                       START OF PUBLIC INTERFACE                       ]]--
-- PlanetaryReference CLASS METHODS:
--
-- BodyParameters - create an instance of BodyParameters class
-- planetarySystemId  [in]: the body's planetary system ID.
-- bodyId             [in]: the body's ID.
-- radius             [in]: the radius in meters of the planetary body.
-- bodyCenter         [in]: the world coordinates of the center (vec3 or table).
-- GM                 [in]: the body's standard gravitational parameter.
-- return: an instance of BodyParameters class.
--
PlanetaryReference.BodyParameters = mkBodyParameters
--
-- MapPosition - create an instance of the MapPosition class
-- overload [in]: either a planetary system ID or a position string ('::pos...')
-- bodyId [in]:   (ignored if overload is a position string) the body's ID.
-- latitude [in]: (ignored if overload is a position string) the latitude.
-- longitude [in]:(ignored if overload is a position string) the longitude.
-- altitude [in]: (ignored if overload is a position string) the altitude.
-- return: the class instance
--
PlanetaryReference.MapPosition    = mkMapPosition
--
-- PlanetarySystem - create an instance of PlanetarySystem class
-- referenceData [in]: a table (indexed by bodyId) of body reference info.
-- return: the class instance
--
PlanetaryReference.PlanetarySystem = mkPlanetarySystem
--
-- createBodyParameters - create an instance of BodyParameters class
-- planetarySystemId  [in]: the body's planetary system ID.
-- bodyId             [in]: the body's ID.
-- surfaceArea        [in]: the body's surface area in square meters.
-- aPosition          [in]: world coordinates of a position near the body.
-- verticalAtPosition [in]: a vector pointing towards the body center.
-- altitudeAtPosition [in]: the altitude in meters at the position.
-- gravityAtPosition  [in]: the magnitude of the gravitational acceleration.
-- return: an instance of BodyParameters class.
--
function PlanetaryReference.createBodyParameters(planetarySystemId,
                                                 bodyId,
                                                 surfaceArea,
                                                 aPosition,
                                                 verticalAtPosition,
                                                 altitudeAtPosition,
                                                 gravityAtPosition)
    assert(isSNumber(planetarySystemId),
           'Argument 1 (planetarySystemId) must be a number:' ..
           type(planetarySystemId))
    assert(isSNumber(bodyId),
           'Argument 2 (bodyId) must be a number:' .. type(bodyId))
    assert(isSNumber(surfaceArea),
           'Argument 3 (surfaceArea) must be a number:' .. type(surfaceArea))
    assert(isTable(aPosition),
           'Argument 4 (aPosition) must be an array or vec3:' ..
           type(aPosition))
    assert(isTable(verticalAtPosition),
           'Argument 5 (verticalAtPosition) must be an array or vec3:' ..
           type(verticalAtPosition))
    assert(isSNumber(altitudeAtPosition),
           'Argument 6 (altitude) must be in meters:' ..
           type(altitudeAtPosition))
    assert(isSNumber(gravityAtPosition),
           'Argument 7 (gravityAtPosition) must be number:' ..
           type(gravityAtPosition))
    local radius   = math.sqrt(surfaceArea/4/math.pi)
    local distance = radius + altitudeAtPosition
    local center   = vec3(aPosition) + distance*vec3(verticalAtPosition)
    local GM       = gravityAtPosition * distance * distance
    return mkBodyParameters(planetarySystemId, bodyId, radius, center, GM)
end
--
-- isMapPosition - check for the presence of the 'MapPosition' fields
-- valueToTest [in]: the value to be checked
-- return: 'true' if all required fields are present in the input value
--
PlanetaryReference.isMapPosition  = isMapPosition
-- PlanetaryReference INSTANCE METHODS:
--
-- getPlanetarySystem - get the planetary system using ID or MapPosition as key
-- overload [in]: either the planetary system ID or a MapPosition that has it.
-- return: instance of 'PlanetarySystem' class or nil on error
--
function PlanetaryReference:getPlanetarySystem(overload)
    --if galaxyAtlas then
        local planetarySystemId = overload
        if isMapPosition(overload) then
            planetarySystemId = overload.systemId
        end
        if type(planetarySystemId) == 'number' then
            local system = self.galaxyAtlas[i]
            if system then
                if getmetatable(nv) ~= PlanetarySystem then
                    system = mkPlanetarySystem(system)
                end
                return system
            end
        end
    --end
    --return nil
end
-- PlanetarySystem INSTANCE METHODS:
--
-- castIntersections - Find the closest body that intersects a "ray cast".
-- origin [in]: the origin of the "ray cast" in world coordinates
-- direction [in]: the direction of the "ray cast" as a 'vec3' instance.
-- sizeCalculator [in]: (default: returns 1.05*radius) Returns size given body.
-- bodyIds[in]: (default: all IDs in system) check only the given IDs.
-- return: The closest body that blocks the cast or 'nil' if none.
--
function PlanetarySystem:castIntersections(origin,
                                           direction,
                                           sizeCalculator,
                                           bodyIds)
    local sizeCalculator = sizeCalculator or
                            function (body) return 1.05*body.radius end
    local candidates = {}
    if bodyIds then
        for _,i in ipairs(bodyIds) do candidates[i] = self[i] end
    else
        bodyIds = {}
        for k,body in pairs(self) do
            table.insert(bodyIds, k)
            candidates[k] = body
        end
    end
    local function compare(b1,b2)
        local v1 = candidates[b1].center - origin
        local v2 = candidates[b2].center - origin
        return v1:len() < v2:len()
    end
    table.sort(bodyIds, compare)
    local dir = direction:normalize()
    for i, id in ipairs(bodyIds) do
        local body   = candidates[id]
        local c_oV3  = body.center - origin
        local radius = sizeCalculator(body)
        local dot    = c_oV3:dot(dir)
        local desc   = dot^2 - (c_oV3:len2() - radius^2)
        if desc >= 0 then
            local root     = math.sqrt(desc)
            local farSide  = dot + root
            local nearSide = dot - root
            if nearSide > 0 then
                return body, farSide, nearSide
            elseif farSide > 0 then
                return body, farSide, nil
            end
        end
    end
    return nil, nil, nil
end
--
-- closestBody - find the closest body to a given set of world coordinates
-- coordinates       [in]: the world coordinates of position in space
-- return: an instance of the BodyParameters object closest to 'coordinates'
--
function PlanetarySystem:closestBody(coordinates)
    assert(type(coordinates) == 'table', 'Invalid coordinates.')
    local minDistance2, body
    local coord = vec3(coordinates)
    for _,params in pairs(self) do
        local distance2 = (params.center - coord):len2()
        if not body or distance2 < minDistance2 then
            body         = params
            minDistance2 = distance2
        end
    end
    return body
end
--
-- convertToBodyIdAndWorldCoordinates - map to body Id and world coordinates
-- overload [in]: an instance of MapPosition or a position string ('::pos...)
-- return: a vec3 instance containing the world coordinates or 'nil' on error.
--
function PlanetarySystem:convertToBodyIdAndWorldCoordinates(overload)
    local mapPosition = overload
    if isString(overload) then
        mapPosition = mkMapPosition(overload)
    end
    if mapPosition.bodyId == 0 then
        return 0, vec3(mapPosition.latitude,
                       mapPosition.longitude,
                       mapPosition.altitude)
    end
    local params = self:getBodyParameters(mapPosition)
    if params then
        return mapPosition.bodyId,
               params:convertToWorldCoordinates(mapPosition)
    end
end
--
-- getBodyParameters - get or create an instance of BodyParameters class
-- overload [in]: either an instance of MapPosition or a body's ID.
-- return: a BodyParameters instance or 'nil' if body ID is not found.
--
function PlanetarySystem:getBodyParameters(overload)
    local bodyId = overload
    if isMapPosition(overload) then
        bodyId = overload.bodyId
    end
    assert(isSNumber(bodyId),
               'Argument 1 (bodyId) must be a number:' .. type(bodyId))
    return self[bodyId]
end
--
-- getPlanetarySystemId - get the planetary system ID for this instance
-- return: the planetary system ID or nil if no planets are in the system.
--
function PlanetarySystem:getPlanetarySystemId()
    local k, v = next(self)
    return v and v.planetarySystemId
end
-- BodyParameters INSTANCE METHODS:
--
-- convertToMapPosition - create an instance of MapPosition from coordinates
-- worldCoordinates [in]: the world coordinates of the map position.
-- return: an instance of MapPosition class
--
function BodyParameters:convertToMapPosition(worldCoordinates)
    assert(isTable(worldCoordinates),
           'Argument 1 (worldCoordinates) must be an array or vec3:' ..
           type(worldCoordinates))
    local worldVec  = vec3(worldCoordinates)
    if self.bodyId == 0 then
        return setmetatable({latitude  = worldVec.x,
                             longitude = worldVec.y,
                             altitude  = worldVec.z,
                             bodyId    = 0,
                             systemId  = self.planetarySystemId}, MapPosition)
    end
    local coords    = worldVec - self.center
    local distance  = coords:len()
    local altitude  = distance - self.radius
    local latitude  = 0
    local longitude = 0
    if not float_eq(distance, 0) then
        local phi = math.atan(coords.y, coords.x)
        longitude = phi >= 0 and phi or (2*math.pi + phi)
        latitude  = math.pi/2 - math.acos(coords.z/distance)
    end
    return setmetatable({latitude  = latitude,
                         longitude = longitude,
                         altitude  = altitude,
                         bodyId    = self.bodyId,
                         systemId  = self.planetarySystemId}, MapPosition)
end
--
-- convertToWorldCoordinates - convert a map position to world coordinates
-- overload [in]: an instance of MapPosition or a position string ('::pos...')
--
function BodyParameters:convertToWorldCoordinates(overload)
    local mapPosition = isString(overload) and
                                           mkMapPosition(overload) or overload
    if mapPosition.bodyId == 0 then -- support deep space map position
        return vec3(mapPosition.latitude,
                    mapPosition.longitude,
                    mapPosition.altitude)
    end
    assert(isMapPosition(mapPosition),
           'Argument 1 (mapPosition) is not an instance of "MapPosition".')
    assert(mapPosition.systemId == self.planetarySystemId,
           'Argument 1 (mapPosition) has a different planetary system ID.')
    assert(mapPosition.bodyId == self.bodyId,
           'Argument 1 (mapPosition) has a different planetary body ID.')
    local xproj = math.cos(mapPosition.latitude)
    return self.center + (self.radius + mapPosition.altitude) *
           vec3(xproj*math.cos(mapPosition.longitude),
                xproj*math.sin(mapPosition.longitude),
                math.sin(mapPosition.latitude))
end
--
-- getAltitude - calculate the altitude of a point given in world coordinates.
-- worldCoordinates [in]: the world coordinates of the point.
-- return: the altitude in meters
--
function BodyParameters:getAltitude(worldCoordinates)
    return (vec3(worldCoordinates) - self.center):len() - self.radius
end
--
-- getDistance - calculate the distance to a point given in world coordinates.
-- worldCoordinates [in]: the world coordinates of the point.
-- return: the distance in meters
--
function BodyParameters:getDistance(worldCoordinates)
    return (vec3(worldCoordinates) - self.center):len()
end
--
-- getGravity - calculate the gravity vector induced by the body.
-- worldCoordinates [in]: the world coordinates of the point.
-- return: the gravity vector in meter/seconds^2
--
function BodyParameters:getGravity(worldCoordinates)
    local radial = self.center - vec3(worldCoordinates) -- directed towards body
    local len2   = radial:len2()
    return (self.GM/len2) * radial/math.sqrt(len2)
end
-- end of module
return setmetatable(PlanetaryReference,
                    { __call = function(_,...)
                                    return mkPlanetaryReference(...)
                               end })
end
function Keplers()
    --[[
  Provides methods for computing orbital information for an object
  Usage:
  Kepler = require('autoconf.custom.kepler')
  alioth = Kepler({ GM=157470826617,
                    bodyId=2,
                    center={x=-8.000,y=-8.000,z=-126303.000},
                    name='Alioth',
                    planetarySystemId=0,
                    radius=126068
                  })
  altitude = 6000
  position = '::pos{0,2,0,0,6000}'
  e, o     = alioth:escapeAndOrbitalSpeed(altitude)
  orbit    = alioth:orbitalParameters(position, {0, o+1, 0})
  print("Eccentricity " .. orbit.eccentricity)
  print("Perihelion " .. orbit.periapsis.altitude)
  print("Max. speed " .. orbit.periapsis.speed)
  print("Circular orbit speed " .. orbit.periapsis.circularOrbitSpeed)
  print("Aphelion "  .. orbit.apoapsis.altitude)
  print("Min. speed " .. orbit.apoapsis.speed)
  print("Orbital period " .. orbit.period)
  --- output:
    Eccentricity 0.0018324307017878
    Perihelion 6000.0
    Max. speed 1092.9462297033
    Circular orbit speed 1091.9462297033
    Aphelion 6484.8994605062
    Min. speed 1088.9480596194
    Orbital period 762.02818214049
  Methods:
    Kepler:escapeAndOrbitalSpeed - for a given celestial body and altitude.
    Kepler:orbitalParameters - for a given massless object and a celestial body.
  Description
  The motion of an object in the vicinity of substantially larger mass is
  in the domain of the "2-body problem". By assuming the object whose motion
  is of interest is of negligable mass simplifies the calculations of:
  the speed to escape the body, the speed of a circular orbit, and the
  parameters defining the orbit of the object (or the lack of orbit as the
  case may be).
  Orbital Parameters:
     periapsis - the closest approach to the planet
      apoapsis - the furthest point from the planet if in orbit (otherwise nil)
  eccentricity - 0 for circular orbits
                <1 for elliptical orbits
                 1 for parabiolic trajectory
                >1 for hyperbolic trajectory
        period - time (in seconds) to complete an orbit
  Also See: planetref.lua
]]--
local vec3       = require('cpml.vec3')
local PlanetRef  = PlanetRef()
local function isString(s) return type(s)   == 'string' end
local function isTable(t)  return type(t)   == 'table'  end
local function float_eq(a,b)
    if a == 0 then return math.abs(b) < 1e-09 end
    if b == 0 then return math.abs(a) < 1e-09 end
    return math.abs(a - b) < math.max(math.abs(a),math.abs(b))*epsilon
end
Kepler = {}
Kepler.__index = Kepler
--
-- escapeAndOrbitalSpeed - speed required to escape and for a circular orbit
-- altitude [in]: the height of the orbit in meters above "sea-level"
-- return: the speed in m/s needed to escape the celestial body and to orbit it.
--
function Kepler:escapeAndOrbitalSpeed(altitude)
    assert(self.body)
    -- P = -GMm/r and KE = mv^2/2 (no lorentz factor used)
    -- mv^2/2 = GMm/r
    -- v^2 = 2GM/r
    -- v = sqrt(2GM/r1)
    local distance = altitude + self.body.radius
    if not float_eq(distance, 0) then
        local orbit = math.sqrt(self.body.GM/distance)
        return math.sqrt(2)*orbit, orbit
    end
    return nil, nil
end
--
-- orbitalParameters: determine the orbital elements for a two-body system.
-- overload [in]: the world coordinates or map coordinates of a massless object.
-- velocity [in]: The velocity of the massless point object in m/s.
-- return: the 6 orbital elements for the massless object.
--
function Kepler:orbitalParameters(overload, velocity)
    assert(self.body)
    assert(isTable(overload) or isString(overload))
    assert(isTable(velocity))
    local pos = (isString(overload) or PlanetRef.isMapPosition(overload)) and
                            self.body:convertToWorldCoordinates(overload) or
                vec3(overload)
    local v   = vec3(velocity)
    local r   = pos - self.body.center
    local v2  = v:len2()
    local d   = r:len()
    local mu  = self.body.GM
    local e   = ((v2 - mu/d)*r - r:dot(v)*v)/mu
    local a   = mu/(2*mu/d - v2)
    local ecc = e:len()
    local dir = e:normalize()
    local pd  = a*(1-ecc)
    local ad  = a*(1+ecc)
    local per = pd*dir + self.body.center
    local apo = ecc <= 1 and -ad*dir + self.body.center or nil
    local trm = math.sqrt(a*mu*(1-ecc*ecc))
    local Period = apo and 2*math.pi*math.sqrt(a^3/mu)
    -- These are great and all, but, I need more.
    local trueAnomaly = math.acos((e:dot(r))/(ecc*d))
    if r:dot(v) < 0 then
        trueAnomaly = -(trueAnomaly - 2*math.pi)
    end
    -- Apparently... cos(EccentricAnomaly) = (cos(trueAnomaly) + eccentricity)/(1 + eccentricity * cos(trueAnomaly))
    local EccentricAnomaly = math.acos((math.cos(trueAnomaly) + ecc)/(1 + ecc * math.cos(trueAnomaly)))
    -- Then.... apparently if this is below 0, we should add 2pi to it
    -- I think also if it's below 0, we're past the apoapsis?
    local timeTau = EccentricAnomaly
    if timeTau < 0 then
        timeTau = timeTau + 2*math.pi
    end
    -- So... time since periapsis...
    -- Is apparently easy if you get mean anomly.  t = M/n where n is mean motion, = 2*pi/Period


    local MeanAnomaly = timeTau - ecc * math.sin(timeTau)
    local TimeSincePeriapsis = MeanAnomaly/(2*math.pi/Period)
    --system.print(MeanAnomaly .. " - " .. TimeSincePeriapsis .. " - " .. Period .. " - " .. EccentricAnomaly .. " - " .. timeTau .. " - " .. trueAnomaly)
    -- Mean anom is 0 at periapsis, positive before it... and positive after it.
    -- I guess this is why I needed to use timeTau and not EccentricAnomaly here

    local TimeToPeriapsis = Period - TimeSincePeriapsis
    local TimeToApoapsis = TimeToPeriapsis + Period/2
    if trueAnomaly - math.pi > 0 then -- TBH I think something's wrong in my formulas because I needed this.
        TimeToPeriapsis = TimeSincePeriapsis
        TimeToApoapsis = TimeToPeriapsis + Period/2
    end
    if TimeToApoapsis > Period then
        TimeToApoapsis = TimeToApoapsis - Period
    end
    return { periapsis       = { position           = per,
                                 speed              = trm/pd,
                                 circularOrbitSpeed = math.sqrt(mu/pd),
                                 altitude           = pd - self.body.radius},
             apoapsis        = apo and
                               { position           = apo,
                                 speed              = trm/ad,
                                 circularOrbitSpeed = math.sqrt(mu/ad),
                                 altitude           = ad - self.body.radius},
             currentVelocity = v,
             currentPosition = pos,
             eccentricity    = ecc,
             period          = Period,
             eccentricAnomaly = EccentricAnomaly,
             meanAnomaly = MeanAnomaly,
             timeToPeriapsis = TimeToPeriapsis,
             timeToApoapsis = TimeToApoapsis
           }
end

local function new(bodyParameters)
    local params = PlanetRef.BodyParameters(bodyParameters.planetarySystemId,
                                            bodyParameters.bodyId,
                                            bodyParameters.radius,
                                            bodyParameters.center,
                                            bodyParameters.GM)
    return setmetatable({body = params}, Kepler)
end
return setmetatable(Kepler, { __call = function(_,...) return new(...) end })
end
function Kinematics()
    --[[
  DualUniverse kinematic equations
  Author: JayleBreak
  Usage (unit.start):
  Kinematics = require('autoconf.custom.kinematics')
  Methods:
   computeAccelerationTime - "relativistic" version of t = (vf - vi)/a
   computeDistanceAndTime - Return distance & time needed to reach final speed.
   computeTravelTime - "relativistic" version of t=(sqrt(2ad+v^2)-v)/a
  Description
  DualUniverse increases the effective mass of constructs as their absolute
  speed increases by using the "lorentz" factor (from relativity) as the scale
  factor.  This results in an upper bound on the absolute speed of constructs
  (excluding "warp" drive) that is set to 30 000 KPH (8 333 MPS). This module
  provides utilities for computing some physical quantities taking this
  scaling into account.
]]--
local Kinematic = {} -- just a namespace
local C       = 30000000/3600
local C2      = C*C
local ITERATIONS = 100 -- iterations over engine "warm-up" period
local function lorentz(v) return 1/math.sqrt(1 - v*v/C2) end
--
-- computeAccelerationTime - "relativistic" version of t = (vf - vi)/a
-- initial      [in]: initial (positive) speed in meters per second.
-- acceleration [in]: constant acceleration until 'finalSpeed' is reached.
-- final        [in]: the speed at the end of the time interval.
-- return: the time in seconds spent in traversing the distance
--
function Kinematic.computeAccelerationTime(initial, acceleration, final)
    -- The low speed limit of following is: t=(vf-vi)/a (from: vf=vi+at)
    local k1 = C*math.asin(initial/C)
    return (C * math.asin(final/C) - k1)/acceleration
end
--
-- computeDistanceAndTime - Return distance & time needed to reach final speed.
-- initial[in]:     Initial speed in meters per second.
-- final[in]:       Final speed in meters per second.
-- restMass[in]:    Mass of the construct at rest in Kg.
-- thrust[in]:      Engine's maximum thrust in Newtons.
-- t50[in]:         (default: 0) Time interval to reach 50% thrust in seconds.
-- brakeThrust[in]: (default: 0) Constant thrust term when braking.
-- return: Distance (in meters), time (in seconds) required for change.
--
function Kinematic.computeDistanceAndTime(initial,
                                          final,
                                          restMass,
                                          thrust,
                                          t50,
                                          brakeThrust)
    -- This function assumes that the applied thrust is colinear with the
    -- velocity. Furthermore, it does not take into account the influence
    -- of gravity, not just in terms of its impact on velocity, but also
    -- its impact on the orientation of thrust relative to velocity.
    -- These factors will introduce (usually) small errors which grow as
    -- the length of the trip increases.
    t50            = t50 or 0
    brakeThrust    = brakeThrust or 0 -- usually zero when accelerating
    local tau0     = lorentz(initial)
    local speedUp  = initial <= final
    local a0       = thrust * (speedUp and 1 or -1)/restMass
    local b0       = -brakeThrust/restMass
    local totA     = a0+b0
    if speedUp and totA <= 0 or not speedUp and totA >= 0 then
        return -1, -1 -- no solution
    end
    local distanceToMax, timeToMax = 0, 0
    -- If, the T50 time is set, then assume engine is at zero thrust and will
    -- reach full thrust in 2*T50 seconds. Thrust curve is given by:
    -- Thrust: F(z)=(a0*(1+sin(z))+2*b0)/2 where z=pi*(t/t50 - 1)/2
    -- Acceleration is given by F(z)/m(z) where m(z) = m/sqrt(1-v^2/c^2)
    -- or v(z)' = (a0*(1+sin(z))+2*b0)*sqrt(1-v(z)^2/c^2)/2
    if a0 ~= 0 and t50 > 0 then
        -- Closed form solution for velocity exists:
        -- v(t) = -c*tan(w)/sqrt(tan(w)^2+1) => w = -asin(v/c)
        -- w=(pi*t*(a0/2+b0)-a0*t50*sin(pi*t/2/t50)+*pi*c*k1)/pi/c
        -- @ t=0, v(0) = vi
        -- pi*c*k1/pi/c = -asin(vi/c)
        -- k1 = asin(vi/c)
        local k1  = math.asin(initial/C)
        local c1  = math.pi*(a0/2+b0)
        local c2  = a0*t50
        local c3  = C*math.pi
        local v = function(t)
            local w  = (c1*t - c2*math.sin(math.pi*t/2/t50) + c3*k1)/c3
            local tan = math.tan(w)
            return C*tan/math.sqrt(tan*tan+1)
        end
        local speedchk = speedUp and function(s) return s >= final end or
                                     function(s) return s <= final end
        timeToMax  = 2*t50
        if speedchk(v(timeToMax)) then
            local lasttime = 0
            while math.abs(timeToMax - lasttime) > 0.5 do
                local t = (timeToMax + lasttime)/2
                if speedchk(v(t)) then
                    timeToMax = t
                else
                    lasttime = t
                end
            end
        end
        -- There is no closed form solution for distance in this case.
        -- Numerically integrate for time t=0 to t=2*T50 (or less)
        local lastv = initial
        local tinc  = timeToMax/ITERATIONS
        for step = 1, ITERATIONS do
            local speed = v(step*tinc)
            distanceToMax = distanceToMax + (speed+lastv)*tinc/2
            lastv = speed
        end
        if timeToMax < 2*t50 then
            return distanceToMax, timeToMax
        end
        initial     = lastv
    end
    -- At full thrust, acceleration only depends on the Lorentz factor:
    -- v(t)' = (F/m(v)) = a*sqrt(1-v(t)^2/c^2) where a = a0+b0
    -- -> v = c*sin((at+k1)/c)
    -- @ t=0, v=vi: k1 = c*asin(vi/c)
    -- -> t = (c*asin(v/c) - k1)/a
    -- x(t)' = c*sin((at+k1)/c)
    -- x = k2 - c^2 cos((at+k1)/c)/a
    -- @ t=0, x=0: k2 = c^2 * cos(k1/c)/a
    local k1       = C*math.asin(initial/C)
    local time     = (C * math.asin(final/C) - k1)/totA
    local k2       = C2 *math.cos(k1/C)/totA
    local distance = k2 - C2 * math.cos((totA*time + k1)/C)/totA
    return distance+distanceToMax, time+timeToMax
end
--
-- computeTravelTime - "relativistic" version of t=(sqrt(2ad+v^2)-v)/a
-- initialSpeed [in]: initial (positive) speed in meters per second
-- acceleration [in]: constant acceleration until 'distance' is traversed
-- distance [in]: the distance traveled in meters
-- return: the time in seconds spent in traversing the distance
--
function Kinematic.computeTravelTime(initial, acceleration, distance)
    -- The low speed limit of following is: t=(sqrt(2ad+v^2)-v)/a
    -- (from: d=vt+at^2/2)
    if distance == 0 then return 0 end
    if acceleration > 0 then
        local k1       = C*math.asin(initial/C)
        local k2       = C2*math.cos(k1/C)/acceleration
        return (C*math.acos(acceleration*(k2 - distance)/C2) - k1)/acceleration
    end
    assert(initial > 0, 'Acceleration and initial speed are both zero.')
    return distance/initial
end
function Kinematic.lorentz(v) return lorentz(v) end
return Kinematic
end
PlanetaryReference = PlanetRef()
galaxyReference = PlanetaryReference(Atlas())
Kinematic = Kinematics()
Kep = Keplers()
function getDistanceDisplayString(distance)
    local su = distance > 100000
    local result = ""
    if su then
        -- Convert to SU
        result = round(distance/1000/200,1) .. " SU"
    else
        -- Convert to KM
        result = round(distance/1000,1) .. " KM"
    end

    return result
end

PlanetaryReference = PlanetRef()
galaxyReference = PlanetaryReference(Atlas())

MapScreenButtons = {}
MapScreenMouseX = 0
MapScreenMouseY = 0
MapScreenMouseDown = false
MapScreenButtonSelected = 0
local worldPos = vec3(core.getConstructWorldPos())
local locX = (worldPos.x/400000)
local locY = (worldPos.y/400000)*(-1)
local destX = 0
local destY = 0
local sudistance = 0
local loc = vec3(core.getConstructWorldPos())
local ion = galaxyReference[0][120]  ---uses Atlas functions
local thades = vec3(29165536.000, 10865536.000, 65536.000)
local sinnen = vec3(58665536.000, 29665536.000, 58165536.000)
local alioth = galaxyReference[0][2]    ---uses Atlas functions
local madis = vec3(17465536.000, 22665536.000, -34464.000)
local jago = vec3(-94134464.000, 12765536.000, -3634464.000)
local symeon = vec3(14165536.000, -85634464.000, -934464.000)
local lacobus = vec3(98865536.000, -13534464.000, -934464.000)
local teoma = vec3(80865536.000, 54665536.000, -934464.000)
local feli = vec3(-43534464.000, 22565536.000, -48934464.000)
local talemai = vec3(-13234464.000, 55765536.000, 465536.000)
local sicari = vec3(52765536.000, 27165536.000, 52065536.000)
local distion = math.floor(ion:getDistance(loc)/200000)   ---uses getDistance functions----
local distthades = string.format("%.2f", math.sqrt((loc.x-thades.x)^2+(loc.y-thades.y)^2+(loc.z-thades.z)^2)/200000)
local distalioth = math.floor(alioth:getDistance(loc)/200000)   ---uses getDistance functions----
local distmadis = string.format("%.2f", math.sqrt((loc.x-madis.x)^2+(loc.y-madis.y)^2+(loc.z-madis.z)^2)/200000)
local distjago = string.format("%.2f", math.sqrt((loc.x-jago.x)^2+(loc.y-jago.y)^2+(loc.z-jago.z)^2)/200000)
local distlacobus = string.format("%.2f", math.sqrt((loc.x-lacobus.x)^2+(loc.y-lacobus.y)^2+(loc.z-lacobus.z)^2)/200000)
local distteoma = string.format("%.2f", math.sqrt((loc.x-teoma.x)^2+(loc.y-teoma.y)^2+(loc.z-teoma.z)^2)/200000)
local distsymeon = string.format("%.2f", math.sqrt((loc.x-symeon.x)^2+(loc.y-symeon.y)^2+(loc.z-symeon.z)^2)/200000)
local distfeli = string.format("%.2f", math.sqrt((loc.x-feli.x)^2+(loc.y-feli.y)^2+(loc.z-feli.z)^2)/200000)
local distsinnen = string.format("%.2f", math.sqrt((loc.x-sinnen.x)^2+(loc.y-sinnen.y)^2+(loc.z-sinnen.z)^2)/200000)
local disttalemai = string.format("%.2f", math.sqrt((loc.x-talemai.x)^2+(loc.y-talemai.y)^2+(loc.z-talemai.z)^2)/200000)
local distsicari = string.format("%.2f", math.sqrt((loc.x-sicari.x)^2+(loc.y-sicari.y)^2+(loc.z-sicari.z)^2)/200000)



      for i = 1,1 do
   local button = {id = ("b"..1), enabled=true, td="<td>", top=2/100, bottom=13/100, left=1/100, right=28/100}
    table.insert(MapScreenButtons, button)
end
  for i = 2,2 do
   local button = {id = ("b"..2), enabled=true, td="<td>", top=15/100, bottom=26/100, left=1/100, right=30/100}
    table.insert(MapScreenButtons, button)
end
    for i = 3,3 do
    local button = {id = ("b"..3), enabled=true, td="<td>", top=27/100, bottom=38/100, left=1/100, right=28/100}
      table.insert(MapScreenButtons, button)
end
    for i = 4,4 do
    local button = {id = ("b"..4), enabled=true, td="<td>", top=39/100, bottom=50/100, left=1/100, right=28/100}
      table.insert(MapScreenButtons, button)
end
    for i = 5,5 do
    local button = {id = ("b"..5), enabled=true, td="<td>", top=51/100, bottom=62/100, left=1/100, right=28/100}
      table.insert(MapScreenButtons, button)
end
    for i = 6,6 do
    local button = {id = ("b"..6), enabled=true, td="<td>", top=64/100, bottom=75/100, left=1/100, right=28/100}
      table.insert(MapScreenButtons, button)
end
    for i = 7,7 do
    local button = {id = ("b"..7), enabled=true, td="<td>", top=2/100, bottom=13/100, left=75/100, right=100/100}
      table.insert(MapScreenButtons, button)
end
    for i = 8,8 do
    local button = {id = ("b"..8), enabled=true, td="<td>", top=15/100, bottom=26/100, left=75/100, right=100/100}
      table.insert(MapScreenButtons, button)
end
    for i = 9,9 do
    local button = {id = ("b"..9), enabled=true, td="<td>", top=27/100, bottom=38/100, left=75/100, right=100/100}
      table.insert(MapScreenButtons, button)
end
    for i = 10,10 do
    local button = {id = ("b"..10), enabled=true, td="<td>", top=39/100, bottom=50/100, left=75/100, right=100/100}
      table.insert(MapScreenButtons, button)
end
    for i = 11,11 do
    local button = {id = ("b"..11), enabled=true, td="<td>", top=51/100, bottom=62/100, left=75/100, right=100/100}
      table.insert(MapScreenButtons, button)
end
    for i = 12,12 do
    local button = {id = ("b"..12), enabled=true, td="<td>", top=64/100, bottom=75/100, left=75/100, right=100/100}
      table.insert(MapScreenButtons, button)
end
    for i = 13,13 do
    local button = {id = ("b"..13), enabled=true, td="<td>", top=90/100, bottom=100/100, left=1/100, right=18/100}
    table.insert(MapScreenButtons, button)
end
function evaluateButtons()
  local selected = 0

  if #MapScreenButtons >= 1 then
 -- Set button styles
       for i, button in ipairs(MapScreenButtons) do
            if button.left < MapScreenMouseX and MapScreenMouseX < button.right and button.top < MapScreenMouseY and MapScreenMouseY < button.bottom then
                if MapScreenMouseDown and MapScreenButtonSelected == i then
                end
                selected = i
            end
            if not button.enabled then
            end

        end
  end
  return selected
end

function onButtonDown(buttonNo)
  local button = MapScreenButtons[buttonNo]
  if not button or not button.enabled then
	return
  end
end
function onButtonUp(buttonNo)
  local button = MapScreenButtons[buttonNo]
  if not button or not button.enabled then
    return
  end
function onClick(buttonNo)
  local button = MapScreenButtons[buttonNo]
  if not button or not button.enabled then
    return
  end
end

local selection = 0

  if buttonNo == 1 then
destX = 0
destY = 0
selection = 1
sudistance = distalioth
  elseif buttonNo == 2 then
destX = 43
destY = -56
sudistance = distmadis
selection = 2
  elseif buttonNo == 3 then
destX = 73
destY = -27
selection = 3
sudistance = distthades
  elseif buttonNo == 4 then
destX = -33
destY = -139
selection = 4
sudistance = disttalemai
  elseif buttonNo == 5 then
destX = -109
destY = -56
selection = 5
sudistance = distfeli
  elseif buttonNo == 6 then
destX = 131
destY = -68
selection = 6
sudistance = distsicari
  elseif buttonNo == 7 then
destX = 35
destY = 214
selection = 7
sudistance = distsymeon
  elseif buttonNo == 8 then
destX = 146
destY = -74
selection = 8
sudistance = distsinnen
  elseif buttonNo == 9 then
destX = -235
destY = -32
selection = 9
sudistance = distjago
  elseif buttonNo == 10 then
destX = 202
destY = -137
selection = 10
sudistance = distteoma
  elseif buttonNo == 11 then
destX = 7
destY = 247
selection = 11
sudistance = distion
  elseif buttonNo == 12 then
destX = 247
destY = 34
selection = 12
sudistance = distlacobus
  elseif buttonNo == 13 then
  unit.exit()
  end
end

function updateScreen()

loc = vec3(core.getConstructWorldPos())

local shipVelocity = vec3(core.getVelocity()):len() * 3.6
local shipAcceleration = vec3(core.getVelocity()):len() * 3.6
local time_to_distance = 0

if selection == 1 then
    alioth = galaxyReference[0][2]    ---uses Atlas functions
    distalioth = math.floor(alioth:getDistance(loc)/200000)   ---uses getDistance functions----
    if shipVelocity > 29998 then
        time_to_distance = distalioth * 200 / shipVelocity
    elseif shipAcceleration == 0 then
        time_to_distance = distalioth * 200 / shipVelocity
    else
        time_to_distance = distalioth * 200 / shipAcceleration
    end
elseif selection == 2 then
    madis = vec3(17465536.000, 22665536.000, -34464.000)
    distmadis = string.format("%.2f", math.sqrt((loc.x-madis.x)^2+(loc.y-madis.y)^2+(loc.z-madis.z)^2)/200000)
    if shipVelocity > 29998 then
        time_to_distance = distmadis * 200 / shipVelocity
    elseif shipAcceleration == 0 then
        time_to_distance = distmadis * 200 / shipVelocity
    else
        time_to_distance = distmadis * 200 / shipAcceleration
    end
elseif selection == 3 then
    thades = vec3(29165536.000, 10865536.000, 65536.000)
    distthades = string.format("%.2f", math.sqrt((loc.x-thades.x)^2+(loc.y-thades.y)^2+(loc.z-thades.z)^2)/200000)
    if shipVelocity > 29998 then
        time_to_distance = distthades * 200 / shipVelocity
    elseif shipAcceleration == 0 then
        time_to_distance = distthades * 200 / shipVelocity
    else
        time_to_distance = distthades * 200 / shipAcceleration
    end
elseif selection == 4 then
    talemai = vec3(-13234464.000, 55765536.000, 465536.000)
    disttalemai = string.format("%.2f", math.sqrt((loc.x-talemai.x)^2+(loc.y-talemai.y)^2+(loc.z-talemai.z)^2)/200000)
    if shipVelocity > 29998 then
        time_to_distance = disttalemai * 200 / shipVelocity
    elseif shipAcceleration == 0 then
        time_to_distance = disttalemai * 200 / shipVelocity
    else
        time_to_distance = disttalemai * 200 / shipAcceleration
    end
elseif selection == 5 then
 feli = vec3(-43534464.000, 22565536.000, -48934464.000)
 distfeli = string.format("%.2f", math.sqrt((loc.x-feli.x)^2+(loc.y-feli.y)^2+(loc.z-feli.z)^2)/200000)
    if shipVelocity > 29998 then
        time_to_distance = distfeli * 200 / shipVelocity
    elseif shipAcceleration == 0 then
        time_to_distance = distfeli * 200 / shipVelocity
    else
        time_to_distance = distfeli  * 200 / shipAcceleration
    end
elseif selection == 6 then
 sicari = vec3(52765536.000, 27165536.000, 52065536.000)
 distsicari = string.format("%.2f", math.sqrt((loc.x-sicari.x)^2+(loc.y-sicari.y)^2+(loc.z-sicari.z)^2)/200000)
    if shipVelocity > 29998 then
        time_to_distance = distsicari * 200 / shipVelocity
    elseif shipAcceleration == 0 then
        time_to_distance = distsicari * 200 / shipVelocity
    else
        time_to_distance = distsicari  * 200 / shipAcceleration
    end
elseif selection == 7 then
 symeon = vec3(14165536.000, -85634464.000, -934464.000)
 distsymeon = string.format("%.2f", math.sqrt((loc.x-symeon.x)^2+(loc.y-symeon.y)^2+(loc.z-symeon.z)^2)/200000)
    if shipVelocity > 29998 then
        time_to_distance = distsymeon * 200 / shipVelocity
    elseif shipAcceleration == 0 then
        time_to_distance = distsymeon * 200 / shipVelocity
    else
        time_to_distance = distsymeon  * 200 / shipAcceleration
    end
elseif selection == 8 then
 sinnen = vec3(58665536.000, 29665536.000, 58165536.000)
 distsinnen = string.format("%.2f", math.sqrt((loc.x-sinnen.x)^2+(loc.y-sinnen.y)^2+(loc.z-sinnen.z)^2)/200000)
    if shipVelocity > 29998 then
        time_to_distance = distsinnen * 200 / shipVelocity
    elseif shipAcceleration == 0 then
        time_to_distance = distsinnen * 200 / shipVelocity
    else
        time_to_distance = distsinnen * 200 / shipAcceleration
    end
elseif selection == 9 then
 jago = vec3(-94134464.000, 12765536.000, -3634464.000)
 distjago = string.format("%.2f", math.sqrt((loc.x-jago.x)^2+(loc.y-jago.y)^2+(loc.z-jago.z)^2)/200000)
    if shipVelocity > 29998 then
        time_to_distance = distjago * 200 / shipVelocity
    elseif shipAcceleration == 0 then
        time_to_distance = distjago * 200 / shipVelocity
    else
        time_to_distance = distjago * 200 / shipAcceleration
    end
elseif selection == 10 then
 teoma = vec3(80865536.000, 54665536.000, -934464.000)
 distteoma = string.format("%.2f", math.sqrt((loc.x-teoma.x)^2+(loc.y-teoma.y)^2+(loc.z-teoma.z)^2)/200000)
    if shipVelocity > 29998 then
        time_to_distance = distteoma * 200 / shipVelocity
    elseif shipAcceleration == 0 then
        time_to_distance = distteoma * 200 / shipVelocity
    else
        time_to_distance = distteoma * 200 / shipAcceleration
    end
elseif selection == 11 then
 ion = galaxyReference[0][120]  ---uses Atlas functions
 distion = math.floor(ion:getDistance(loc)/200000)   ---uses getDistance functions----
    if shipVelocity > 29998 then
        time_to_distance = distion * 200 / shipVelocity
    elseif shipAcceleration == 0 then
        time_to_distance = distion * 200 / shipVelocity
    else
        time_to_distance = distion * 200 / shipAcceleration
    end
elseif selection == 12 then
 lacobus = vec3(98865536.000, -13534464.000, -934464.000)
 distlacobus = string.format("%.2f", math.sqrt((loc.x-lacobus.x)^2+(loc.y-lacobus.y)^2+(loc.z-lacobus.z)^2)/200000)
    if shipVelocity > 29998 then
        time_to_distance = distlacobus * 200 / shipVelocity
    elseif shipAcceleration == 0 then
        time_to_distance = distlacobus * 200 / shipVelocity
    else
        time_to_distance = distlacobus * 200 / shipAcceleration
    end
else
    if shipVelocity > 29998 then
        time_to_distance = sudistance * 200 / shipVelocity
    elseif shipAcceleration == 0 then
        time_to_distance = sudistance * 200 / shipVelocity
    else
        time_to_distance = sudistance * 200 / shipAcceleration
    end
end

warpmath = math.floor(math.floor(core.getConstructMass()/ 1000)  * sudistance * 0.00025)

html= ([[
<svg width="1024" height="1024" viewBox="0 0 1024 1640"><circle cx="500" cy="500" r="400" stroke="darkgreen" stroke-width="3" transform=""></circle><circle cx="500" cy="500" r="350" stroke="#FFF" stroke-width="3" transform="" stroke-opacity="0.2"></circle><circle cx="500" cy="500" r="300" stroke="#FFF" stroke-width="3" transform=""></circle><circle cx="500" cy="500" r="250" stroke="#FFF" stroke-width="3" transform="" stroke-opacity="0.2"></circle><circle cx="500" cy="500" r="200" stroke="#FFF" stroke-width="3" transform=""></circle><circle cx="500" cy="500" r="150" stroke="#FFF" stroke-width="3" transform="" stroke-opacity="0.2"></circle><circle cx="500" cy="500" r="100" stroke="lightblue" stroke-width="3" transform=""></circle><circle cx="500" cy="500" r="50" stroke="lightblue" stroke-width="3" transform="" stroke-opacity="0.2"></circle><circle cx="500" cy="500" r="20" stroke="Orange" stroke-width="2" transform=""></circle><text x="510" y="510" fill="Yellow">Helios</text><circle cx="-0.00" cy="0" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="-0.00" y="0" transform="translate(500,480)" fill="white" font-size="20">Alioth</text><circle cx="7.16" cy="247.59" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="7.16" y="247.59" transform="translate(480,480)" fill="white" font-size="20">Ion</text><circle cx="35.41" cy="214.09" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="35.41" y="214.09" transform="translate(500,480)" fill="white" font-size="20">Symeon</text><circle cx="-33.09" cy="-139.41" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="-33.09" y="-139.41" transform="translate(500,480)" fill="white" font-size="20">Talemai</text><circle cx="202.16" cy="-136.66" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="202.16" y="-136.66" transform="translate(500,480)" fill="white" font-size="20">Teoma</text><circle cx="247.16" cy="33.84" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="247.16" y="33.84" transform="translate(500,480)" fill="white" font-size="20">Lacobus</text><circle cx="-108.84" cy="-56.41" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="-108.84" y="-56.41" transform="translate(500,480)" fill="white" font-size="20">Feli</text><circle cx="72.91" cy="-27.16" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="72.91" y="-27.16" transform="translate(500,485)" fill="white" font-size="20">Thades</text><circle cx="43.66" cy="-56.66" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="43.66" y="-56.66" transform="translate(500,480)" fill="white" font-size="20">Madis</text><circle cx="-235.34" cy="-31.91" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="-235.34" y="-31.91" transform="translate(500,480)" fill="white" font-size="20">Jago</text><circle cx="131.91" cy="-67.91" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="131.91" y="-67.91" transform="translate(475,480)" fill="white" font-size="20">Sicari</text><circle cx="146.66" cy="-74.16" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="146.66" y="-74.16" transform="translate(515,480)" fill="white" font-size="20">Sinnen</text>
<line stroke-linecap="undefined" stroke-linejoin="undefined" id="svg_1" y2="]]..destY..[[" x2="]]..destX..[[" y1="]]..locY..[[" x1="]]..locX..[[" transform="translate(500,500)" stroke-width="5" stroke="#ff0000" fill="none"/>
<circle cx="]]..locX..[[" cy="]]..locY..[[" r="3" stroke="black" stroke-width="1" fill="limegreen" transform="translate(500,500)"></circle>
<text x="]]..locX..[[" y="]]..locY..[[" transform="translate(500,500)"
fill="limegreen" font-size= "4.5vh" font-weight= "bold">//SHIP POSITION</text>
</svg>
<svg width="1024" height="612" xmlns="http://www.w3.org/2000/svg">>
 <g>
  <title>Layer 1</title>
  <g id="svg_24">
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_8" y="70" x="55" stroke-width="0" fill="Yellow">Alioth  :]]..distalioth..[[ SU</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_14" y="170" x="55" stroke-width="0" fill="Yellow">Madis  :]]..distmadis..[[ SU</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_17" y="270" x="55" stroke-width="0" fill="Yellow">Thades  :]]..distthades..[[ SU</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_20" y="370" x="55" stroke-width="0" fill="Yellow">Talemai  :]]..disttalemai..[[ SU</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_23" y="470" x="55" stroke-width="0" fill="Yellow">Feli  :]]..distfeli..[[ SU</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_26" y="570" x="55" stroke-width="0" fill="Yellow">Sicari  :]]..distsicari..[[ SU</text>
   <g id="svg_12">
    <rect rx="10" id="svg_1" height="50" width="250" y="30" x="15" stroke-width="10" stroke="#FFF" fill="none"/>
    <rect rx="10" id="svg_3" height="50" width="250" y="105" x="15" stroke-width="10" stroke="#FFF" fill="none"/>
    <rect rx="10" id="svg_7" height="50" width="250" y="180" x="15" stroke-width="10" stroke="#FFF" fill="none"/>
    <rect rx="10" id="svg_9" height="50" width="250" y="255" x="15" stroke-width="10" stroke="#FFF" fill="none"/>
    <rect rx="10" id="svg_10" height="50" width="250" y="330" x="15" stroke-width="10" stroke="#FFF" fill="none"/>
    <rect rx="10" id="svg_11" height="50" width="250" y="405" x="15" stroke-width="10" stroke="#FFF" fill="none"/>
   </g>
  </g>
  <g id="svg_40">
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_25" y="70" x="997.163642" stroke-width="0" fill="Yellow">Symeon  :]]..distsymeon..[[ SU</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_27" y="170" x="997.163642" stroke-width="0" fill="Yellow">Sinnen  :]]..distsinnen..[[ SU</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_28" y="270" x="997.163642" stroke-width="0" fill="Yellow">Jago  :]]..distjago..[[ SU</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_30" y="370" x="997.163642" stroke-width="0" fill="Yellow">Teoma  :]]..distteoma..[[ SU</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_31" y="470" x="997.163642" stroke-width="0" fill="Yellow">Ion  :]]..distion..[[ SU</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_32" y="570" x="997.163642" stroke-width="0" fill="Yellow">Lacobus  :]]..distlacobus..[[ SU</text>
   <g id="svg_39">
    <rect rx="10" id="svg_33" height="50" width="250" y="30" x="760" stroke-width="10" stroke="#FFF" fill="none"/>
    <rect rx="10" id="svg_34" height="50" width="250" y="105" x="760" stroke-width="10" stroke="#FFF" fill="none"/>
    <rect rx="10" id="svg_35" height="50" width="250" y="180" x="760" stroke-width="10" stroke="#FFF" fill="none"/>
    <rect rx="10" id="svg_36" height="50" width="250" y="255" x="760" stroke-width="10" stroke="#FFF" fill="none"/>
    <rect rx="10" id="svg_37" height="50" width="250" y="330" x="760" stroke-width="10" stroke="#FFF" fill="none"/>
    <rect rx="10" id="svg_38" height="50" width="250" y="405" x="760" stroke-width="10" stroke="#FFF" fill="none"/>
   </g>
  </g>
 </g>
  <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="30" id="svg_32" y="700" x="20" stroke-width="0" fill="LightBlue">Est. Warp Cost: ]]..warpmath..[[</text>
  <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="30" id="svg_32" y="750" x="20" stroke-width="0" fill="LightBlue">Construct Weight: ]]..math.floor(core.getConstructMass()/ 1000)..[[ tons</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="30" id="svg_32" y="700" x="500" stroke-width="0" fill="LightBlue">TTD: ]]..time_to_distance..[[ hrs</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="30" id="svg_32" y="750" x="500" stroke-width="0" fill="LightBlue">Vel: ]]..shipVelocity..[[ km/h</text>
</svg>

    ]])

screen.setHTML(html)
screen2.setHTML(html)
end
unit.setTimer("spacemap",.08)

 

Link to comment
Share on other sites

I updated the math and fixed calculation issue, I will add rounding next..

 

 

function Atlas()
        return {
    [0] = {
      [1]={
        GM=6930729684,
        bodyId=1,
        center={x=17465536.000,y=22665536.000,z=-34464.000},
        name='Madis',
        planetarySystemId=0,
        radius=44300
      },
      [2]={
        GM=157470826617,
        bodyId=2,
        center={x=-8.000,y=-8.000,z=-126303.000},
        name='Alioth',
        planetarySystemId=0,
        radius=126068
      },
      [3]={
        GM=11776905000,
        bodyId=3,
        center={x=29165536.000,y=10865536.000,z=65536.000},
        name='Thades',
        planetarySystemId=0,
        radius=49000
      },
      [4]={
        GM=14893847582,
        bodyId=4,
        center={x=-13234464.000,y=55765536.000,z=465536.000},
        name='Talemai',
        planetarySystemId=0,
        radius=57450
      },
      [5]={
        GM=16951680000,
        bodyId=5,
        center={x=-43534464.000,y=22565536.000,z=-48934464.000},
        name='Feli',
        planetarySystemId=0,
        radius=60000
      },
      [6]={
        GM=10502547741,
        bodyId=6,
        center={x=52765536.000,y=27165538.000,z=52065535.000},
        name='Sicari',
        planetarySystemId=0,
        radius=51100
      },
      [7]={
        GM=13033380591,
        bodyId=7,
        center={x=58665538.000,y=29665535.000,z=58165535.000},
        name='Sinnen',
        planetarySystemId=0,
        radius=54950
      },
      [8]={
        GM=18477723600,
        bodyId=8,
        center={x=80865538.000,y=54665536.000,z=-934463.940},
        name='Teoma',
        planetarySystemId=0,
        radius=62000
      },
      [9]={
        GM=18606274330,
        bodyId=9,
        center={x=-94134462.000,y=12765534.000,z=-3634464.000},
        name='Jago',
        planetarySystemId=0,
        radius=61590
      },
      [10]={
        GM=78480000,
        bodyId=10,
        center={x=17448118.224,y=22966846.286,z=143078.820},
        name='Madis Moon 1',
        planetarySystemId=0,
        radius=10000
      },
      [11]={
        GM=237402000,
        bodyId=11,
        center={x=17194626.000,y=22243633.880,z=-214962.810},
        name='Madis Moon 2',
        planetarySystemId=0,
        radius=11000
      },
      [12]={
        GM=265046609,
        bodyId=12,
        center={x=17520614.000,y=22184730.000,z=-309989.990},
        name='Madis Moon 3',
        planetarySystemId=0,
        radius=15005
      },
      [21]={
        GM=2118960000,
        bodyId=21,
        center={x=457933.000,y=-1509011.000,z=115524.000},
        name='Alioth Moon 1',
        planetarySystemId=0,
        radius=30000
      },
      [22]={
        GM=2165833514,
        bodyId=22,
        center={x=-1692694.000,y=729681.000,z=-411464.000},
        name='Alioth Moon 4',
        planetarySystemId=0,
        radius=30330
      },
      [26]={
        GM=68234043600,
        bodyId=26,
        center={x=-1404835.000,y=562655.000,z=-285074.000},
        name='Sanctuary',
        planetarySystemId=0,
        radius=83400
      },
      [30]={
        GM=211564034,
        bodyId=30,
        center={x=29214402.000,y=10907080.695,z=433858.200},
        name='Thades Moon 1',
        planetarySystemId=0,
        radius=14002
      },
      [31]={
        GM=264870000,
        bodyId=31,
        center={x=29404193.000,y=10432768.000,z=19554.131},
        name='Thades Moon 2',
        planetarySystemId=0,
        radius=15000
      },
      [40]={
        GM=141264000,
        bodyId=40,
        center={x=-13503090.000,y=55594325.000,z=769838.640},
        name='Talemai Moon 2',
        planetarySystemId=0,
        radius=12000
      },
      [41]={
        GM=106830900,
        bodyId=41,
        center={x=-12800515.000,y=55700259.000,z=325207.840},
        name='Talemai Moon 3',
        planetarySystemId=0,
        radius=11000
      },
      [42]={
        GM=264870000,
        bodyId=42,
        center={x=-13058408.000,y=55781856.000,z=740177.760},
        name='Talemai Moon 1',
        planetarySystemId=0,
        radius=15000
      },
      [50]={
        GM=499917600,
        bodyId=50,
        center={x=-43902841.780,y=22261034.700,z=-48862386.000},
        name='Feli Moon 1',
        planetarySystemId=0,
        radius=14000
      },
      [70]={
        GM=396912600,
        bodyId=70,
        center={x=58969616.000,y=29797945.000,z=57969449.000},
        name='Sinnen Moon 1',
        planetarySystemId=0,
        radius=17000
      },
      [100]={
        GM=13975172474,
        bodyId=100,
        center={x=98865536.000,y=-13534464.000,z=-934461.990},
        name='Lacobus',
        planetarySystemId=0,
        radius=55650
      },
      [101]={
        GM=264870000,
        bodyId=101,
        center={x=98905288.170,y=-13950921.100,z=-647589.530},
        name='Lacobus Moon 3',
        planetarySystemId=0,
        radius=15000
      },
      [102]={
        GM=444981600,
        bodyId=102,
        center={x=99180968.000,y=-13783862.000,z=-926156.400},
        name='Lacobus Moon 1',
        planetarySystemId=0,
        radius=18000
      },
      [103]={
        GM=211503600,
        bodyId=103,
        center={x=99250052.000,y=-13629215.000,z=-1059341.400},
        name='Lacobus Moon 2',
        planetarySystemId=0,
        radius=14000
      },
      [110]={
        GM=9204742375,
        bodyId=110,
        center={x=14165536.000,y=-85634465.000,z=-934464.300},
        name='Symeon',
        planetarySystemId=0,
        radius=49050
      },
      [120]={
        GM=7135606629,
        bodyId=120,
        center={x=2865536.700,y=-99034464.000,z=-934462.020},
        name='Ion',
        planetarySystemId=0,
        radius=44950
      },
      [121]={
        GM=106830900,
        bodyId=121,
        center={x=2472916.800,y=-99133747.000,z=-1133582.800},
        name='Ion Moon 1',
        planetarySystemId=0,
        radius=11000
      },
      [122]={
        GM=176580000,
        bodyId=122,
        center={x=2995424.500,y=-99275010.000,z=-1378480.700},
        name='Ion Moon 2',
        planetarySystemId=0,
        radius=15000
      }
     }
    }
    end
function PlanetRef()
--[[
  Provide coordinate transforms and access to kinematic related parameters
  Author: JayleBreak
  Usage (unit.start):
  PlanetaryReference = require('planetref')
  galaxyReference = PlanetaryReference(referenceTableSource)
  helios = galaxyReference[0] -- PlanetaryReference.PlanetarySystem instance
  alioth = helios[2]          -- PlanetaryReference.BodyParameters instance
  Methods:
    PlanetaryReference:getPlanetarySystem - based on planetary system ID.
    PlanetaryReference.isMapPosition - 'true' if an instance of 'MapPosition'
    PlanetaryReference.createBodyParameters - for entry into reference table
    PlanetaryReference.BodyParameters - a class containing a body's information.
    PlanetaryReference.MapPosition - a class for map coordinates
    PlanetaryReference.PlanetarySystem - a container for planetary system info.
    PlanetarySystem:castIntersections - from a position in a given direction.
    PlanetarySystem:closestBody - to the specified coordinates.
    PlanetarySystem:convertToBodyIdAndWorldCoordinates - from map coordinates.
    PlanetarySystem:getBodyParameters - from reference table.
    PlanetarySystem:getPlanetarySystemId - for the instance.
    BodyParameters:convertToWorldCoordinates - from map coordinates
    BodyParameters:convertToMapPosition - from world coordinates
    BodyParameters:getAltitude - of world coordinates
    BodyParameters:getDistance - from center to world coordinates
    BodyParameters:getGravity - at a given position in world coordinates.
  Description
  An instance of the 'PlanetaryReference' "class" can contain transform and
  kinematic reference information for all planetary systems in DualUniverse.
  Each planetary system is identified by a numeric identifier. Currently,
  the only planetary system, Helios, has the identifier: zero. This "class"
  supports the indexing ('[]') operation which is equivalent to the
  use of the 'getPlanetarySystem' method. It also supports the 'pairs()'
  method for iterating over planetary systems.

  An instance of the 'PlanetarySystem' "class" contains all reference
  information for a specific system. It supports the indexing ('[]') and
  'pairs()' functions which allows iteration over each "body" in the
  system where the key is the numeric body ID. It also supports the
  'tostring()' method.
  An instance of the 'BodyParameters' "class" contains all reference
  information for a single celestial "body" (a moon or planet). It supports
  the 'tostring()' method, and contains the data members:
          planetarySystemId - numeric planetary system ID
          bodyId            - numeric body ID
          radius            - radius of the body in meters (zero altitude)
          center            - world coordinates of the body's center position
          GM                - the gravitation parameter (g = GM/radius^2)
  Note that the user is allowed to add custom fields (e.g. body name), but
  should insure that complex table values have the '__tostring' metamethod
  implemented.
  Transform and Kinematics:
  "World" coordinates is a cartesian coordinate system with an origin at an
  arbitrary fixed point in a planetary system and with distances measured in
  meters. The coordinates are expressible either as a simple table of 3 values
  or an instance of the 'vec3' class.  In either case, the planetary system
  identity is implicit.
  "Map" coordinates is a geographic coordinate system with an origin at the
  center of an identified (by a numeric value) celestial body which is a
  member of an identified (also a numeric value) planetary system. Note that
  the convention that latitude, longitude, and altitude values will be the
  position's x, y, and z world coordinates in the special case of body ID 0.
  The kinematic parameters in the reference data permit calculations of the
  gravitational attraction of the celestial body on other objects.
  Reference Data:
  This is an example of reference data with a single entry assigned to
  planetary system ID 0, and body ID 2 ('Alioth'):
    referenceTable = {
          [0] = { [2] = { planetarySystemId = 0,
                          bodyId = 2,
                          radius = 126068,
                          center = vec3({x=-8, y=-8, z=-126303}),
                          GM = 1.572199+11 } -- as in F=-GMm/r^2
          }
      }
    ref=PlanetaryReference(referenceTable)
  Collecting Reference Data:
  A combination of information from the "Map" screen in the DU user interface,
  and values reported by the DU Lua API can be the source of the reference
  table's data (planetarySystemId, bodyId, and surfaceArea is from the user
  interface):
    referenceTable = {}
    referenceTable[planetarySystemId][bodyId] =
         PlanetaryReference.createBodyParameters(planetarySystemId,
                                                 bodyId,
                                                 surfaceArea,
                                                 core.getConstructWorldPos(),
                                                 core.getWorldVertical(),
                                                 core.getAltitude(),
                                                 core.g())
  Adapting Data Sources:
  Other sources of data can be adapted or converted. An example of adapting a
  table, defined in the file: 'planets.lua', containing information on a single
  planetary system and using celestial body name as the key follows (note that
  a 'name' field is added to the BodyParameters instance transparently after
  construction, and the '__pairs' meta function is required to support the
  'closestBody' and '__tostring' methods):
    ref=PlanetaryReference(
        {[0] = setmetatable(require('planets'),
                        { __index = function(bodies, bodyId)
                             for _,v in pairs(bodies) do
                                 if v and v.bodyId == bodyId then return v end
                             end
                             return nil
                           end,
                         __pairs = function(bodies)
                             return function(t, k)
                                     local nk, nv = next(t, k)
                                     if nv then
                                         local GM = nv.gravity * nv.radius^2
                                         local bp = BodyParameters(0,
                                                                   nv.id,
                                                                   nv.radius,
                                                                   nv.pos,
                                                                   GM)
                                         bp.name = nk
                                         return nk, bp
                                    end
                                    return nk, nv
                                 end, bodies, nil
                           end })
    })

  Converting Data Sources:
  An instance of 'PlanetaryReference' that has been adapted to a data source
  can be used to convert that source to simple table. For example,
  using the adapted instance shown above:
    load('convertedData=' .. tostring(ref))()
    newRef=PlanetaryReference(convertedData)
  Also See: kepler.lua
  ]]--
--[[                    START OF LOCAL IMPLEMENTATION DETAILS             ]]--
-- Type checks
local function isNumber(n)  return type(n)           == 'number' end
local function isSNumber(n) return type(tonumber(n)) == 'number' end
local function isTable(t)   return type(t)           == 'table'  end
local function isString(s)  return type(s)           == 'string' end
local function isVector(v)  return isTable(v)
                                    and isNumber(v.x and v.y and v.z) end
local function isMapPosition(m) return isTable(m) and isNumber(m.latitude  and
                                                               m.longitude and
                                                               m.altitude  and
                                                               m.bodyId    and
                                                               m.systemId) end
-- Constants
local deg2rad    = math.pi/180
local rad2deg    = 180/math.pi
local epsilon    = 1e-10
local num        = ' *([+-]?%d+%.?%d*e?[+-]?%d*)'
local posPattern = '::pos{' .. num .. ',' .. num .. ',' ..  num .. ',' ..
                   num ..  ',' .. num .. '}'
-- Utilities
local utils  = require('cpml.utils')
local vec3   = require('cpml.vec3')
local clamp  = utils.clamp
local function float_eq(a,b)
    if a == 0 then return math.abs(b) < 1e-09 end
    if b == 0 then return math.abs(a) < 1e-09 end
    return math.abs(a - b) < math.max(math.abs(a),math.abs(b))*epsilon
end
local function formatNumber(n)
    local result = string.gsub(
                    string.reverse(string.format('%.4f',n)),
                    '^0*%.?','')
    return result == '' and '0' or string.reverse(result)
end
local function formatValue(obj)
    if isVector(obj) then
        return string.format('{x=%.3f,y=%.3f,z=%.3f}', obj.x, obj.y, obj.z)
    end
    if isTable(obj) and not getmetatable(obj) then
        local list = {}
        local nxt  = next(obj)
        if type(nxt) == 'nil' or nxt == 1 then -- assume this is an array
            list = obj
        else
            for k,v in pairs(obj) do
                local value = formatValue(v)
                if type(k) == 'number' then
                    table.insert(list, string.format('[%s]=%s', k, value))
                else
                    table.insert(list, string.format('%s=%s',   k, value))
                end
            end
        end
        return string.format('{%s}', table.concat(list, ','))
    end
    if isString(obj) then
        return string.format("'%s'", obj:gsub("'",[[\']]))
    end
    return tostring(obj)
end
-- CLASSES
-- BodyParameters: Attributes of planetary bodies (planets and moons)
local BodyParameters = {}
BodyParameters.__index = BodyParameters
BodyParameters.__tostring =
    function(obj, indent)
        local sep = indent or ''
        local keys = {}
        for k in pairs(obj) do table.insert(keys, k) end
        table.sort(keys)
        local list = {}
        for _, k in ipairs(keys) do
            local value = formatValue(obj[k])
            if type(k) == 'number' then
                table.insert(list, string.format('[%s]=%s', k, value))
            else
                table.insert(list, string.format('%s=%s', k, value))
            end
        end
        if indent then
            return string.format('%s%s',
                                 indent,
                                 table.concat(list, ',\n' .. indent))
        end
        return string.format('{%s}', table.concat(list, ','))
    end
BodyParameters.__eq = function(lhs, rhs)
        return lhs.planetarySystemId == rhs.planetarySystemId and
               lhs.bodyId            == rhs.bodyId            and
               float_eq(lhs.radius, rhs.radius)               and
               float_eq(lhs.center.x, rhs.center.x)           and
               float_eq(lhs.center.y, rhs.center.y)           and
               float_eq(lhs.center.z, rhs.center.z)           and
               float_eq(lhs.GM, rhs.GM)
    end
local function mkBodyParameters(systemId, bodyId, radius, worldCoordinates, GM)
    -- 'worldCoordinates' can be either table or vec3
    assert(isSNumber(systemId),
           'Argument 1 (planetarySystemId) must be a number:' .. type(systemId))
    assert(isSNumber(bodyId),
           'Argument 2 (bodyId) must be a number:' .. type(bodyId))
    assert(isSNumber(radius),
           'Argument 3 (radius) must be a number:' .. type(radius))
    assert(isTable(worldCoordinates),
           'Argument 4 (worldCoordinates) must be a array or vec3.' ..
           type(worldCoordinates))
    assert(isSNumber(GM),
           'Argument 5 (GM) must be a number:' .. type(GM))
    return setmetatable({planetarySystemId = tonumber(systemId),
                         bodyId            = tonumber(bodyId),
                         radius            = tonumber(radius),
                         center            = vec3(worldCoordinates),
                         GM                = tonumber(GM) }, BodyParameters)
end
-- MapPosition: Geographical coordinates of a point on a planetary body.
local MapPosition = {}
MapPosition.__index = MapPosition
MapPosition.__tostring = function(p)
        return string.format('::pos{%d,%d,%s,%s,%s}',
                             p.systemId,
                             p.bodyId,
                             formatNumber(p.latitude*rad2deg),
                             formatNumber(p.longitude*rad2deg),
                             formatNumber(p.altitude))
    end
MapPosition.__eq       = function(lhs, rhs)
        return lhs.bodyId   == rhs.bodyId              and
               lhs.systemId == rhs.systemId            and
               float_eq(lhs.latitude,   rhs.latitude)  and
               float_eq(lhs.altitude,   rhs.altitude)  and
               (float_eq(lhs.longitude, rhs.longitude) or
                float_eq(lhs.latitude, math.pi/2)      or
                float_eq(lhs.latitude, -math.pi/2))
    end
-- latitude and longitude are in degrees while altitude is in meters
local function mkMapPosition(overload, bodyId, latitude, longitude, altitude)
    local systemId = overload -- Id or '::pos{...}' string
    if isString(overload) and not longitude and not altitude and
                              not bodyId    and not latitude then
        systemId, bodyId, latitude, longitude, altitude =
                                            string.match(overload, posPattern)
        assert(systemId, 'Argument 1 (position string) is malformed.')
    else
        assert(isSNumber(systemId),
               'Argument 1 (systemId) must be a number:' .. type(systemId))
        assert(isSNumber(bodyId),
               'Argument 2 (bodyId) must be a number:' .. type(bodyId))
        assert(isSNumber(latitude),
               'Argument 3 (latitude) must be in degrees:' .. type(latitude))
        assert(isSNumber(longitude),
               'Argument 4 (longitude) must be in degrees:' .. type(longitude))
        assert(isSNumber(altitude),
               'Argument 5 (altitude) must be in meters:' .. type(altitude))
    end
    systemId  = tonumber(systemId)
    bodyId    = tonumber(bodyId)
    latitude  = tonumber(latitude)
    longitude = tonumber(longitude)
    altitude  = tonumber(altitude)
    if bodyId == 0 then -- this is a hack to represent points in space
        return setmetatable({latitude  = latitude,
                             longitude = longitude,
                             altitude  = altitude,
                             bodyId    = bodyId,
                             systemId  = systemId}, MapPosition)
    end
    return setmetatable({latitude  = deg2rad*clamp(latitude, -90, 90),
                         longitude = deg2rad*(longitude % 360),
                         altitude  = altitude,
                         bodyId    = bodyId,
                         systemId  = systemId}, MapPosition)
end
-- PlanetarySystem - map body IDs to BodyParameters
local PlanetarySystem = {}
PlanetarySystem.__index = PlanetarySystem
PlanetarySystem.__tostring =
    function (obj, indent)
        local sep = indent and (indent .. '  ' )
        local bdylist = {}
        local keys = {}
        for k in pairs(obj) do table.insert(keys, k) end
        table.sort(keys)
        for _, bi in ipairs(keys) do
            bdy = obj[bi]
            local bdys = BodyParameters.__tostring(bdy, sep)
            if indent then
                table.insert(bdylist,
                             string.format('[%s]={\n%s\n%s}',
                                           bi, bdys, indent))
            else
                table.insert(bdylist, string.format('  [%s]=%s', bi, bdys))
            end
        end
        if indent then
            return string.format('\n%s%s%s',
                                 indent,
                                 table.concat(bdylist, ',\n' .. indent),
                                 indent)
        end
        return string.format('{\n%s\n}', table.concat(bdylist, ',\n'))
    end
local function mkPlanetarySystem(referenceTable)
    local atlas = {}
    local pid
    for _, v in pairs(referenceTable) do
        local id = v.planetarySystemId
        if type(id) ~= 'number' then
            error('Invalid planetary system ID: ' .. tostring(id))
        elseif pid and id ~= pid then
            error('Mismatch planetary system IDs: ' .. id .. ' and '
                  .. pid)
        end
        local bid = v.bodyId
        if type(bid) ~= 'number' then
            error('Invalid body ID: ' .. tostring(bid))
        elseif atlas[bid] then
            error('Duplicate body ID: ' .. tostring(bid))
        end
        setmetatable(v.center, getmetatable(vec3.unit_x))
        atlas[bid] = setmetatable(v, BodyParameters)
        pid = id
    end
    return setmetatable(atlas, PlanetarySystem)
end
-- PlanetaryReference - map planetary system ID to PlanetarySystem
PlanetaryReference = {}
local function mkPlanetaryReference(referenceTable)
    return setmetatable({ galaxyAtlas = referenceTable or {} },
                          PlanetaryReference)
end
PlanetaryReference.__index        =
    function(t,i)
        if type(i) == 'number' then
            local system = t.galaxyAtlas[i]
            return mkPlanetarySystem(system)
        end
        return rawget(PlanetaryReference, i)
    end
PlanetaryReference.__pairs        =
    function(obj)
        return  function(t, k)
                    local nk, nv = next(t, k)
                    return nk, nv and mkPlanetarySystem(nv)
                end, obj.galaxyAtlas, nil
    end
PlanetaryReference.__tostring     =
    function (obj)
        local pslist = {}
        for _,ps in pairs(obj or {}) do
            local psi = ps:getPlanetarySystemId()
            local pss = PlanetarySystem.__tostring(ps, '    ')
            table.insert(pslist,
                         string.format('  [%s]={%s\n  }', psi, pss))
        end
        return string.format('{\n%s\n}\n', table.concat(pslist,',\n'))
    end
--[[                       START OF PUBLIC INTERFACE                       ]]--
-- PlanetaryReference CLASS METHODS:
--
-- BodyParameters - create an instance of BodyParameters class
-- planetarySystemId  [in]: the body's planetary system ID.
-- bodyId             [in]: the body's ID.
-- radius             [in]: the radius in meters of the planetary body.
-- bodyCenter         [in]: the world coordinates of the center (vec3 or table).
-- GM                 [in]: the body's standard gravitational parameter.
-- return: an instance of BodyParameters class.
--
PlanetaryReference.BodyParameters = mkBodyParameters
--
-- MapPosition - create an instance of the MapPosition class
-- overload [in]: either a planetary system ID or a position string ('::pos...')
-- bodyId [in]:   (ignored if overload is a position string) the body's ID.
-- latitude [in]: (ignored if overload is a position string) the latitude.
-- longitude [in]:(ignored if overload is a position string) the longitude.
-- altitude [in]: (ignored if overload is a position string) the altitude.
-- return: the class instance
--
PlanetaryReference.MapPosition    = mkMapPosition
--
-- PlanetarySystem - create an instance of PlanetarySystem class
-- referenceData [in]: a table (indexed by bodyId) of body reference info.
-- return: the class instance
--
PlanetaryReference.PlanetarySystem = mkPlanetarySystem
--
-- createBodyParameters - create an instance of BodyParameters class
-- planetarySystemId  [in]: the body's planetary system ID.
-- bodyId             [in]: the body's ID.
-- surfaceArea        [in]: the body's surface area in square meters.
-- aPosition          [in]: world coordinates of a position near the body.
-- verticalAtPosition [in]: a vector pointing towards the body center.
-- altitudeAtPosition [in]: the altitude in meters at the position.
-- gravityAtPosition  [in]: the magnitude of the gravitational acceleration.
-- return: an instance of BodyParameters class.
--
function PlanetaryReference.createBodyParameters(planetarySystemId,
                                                 bodyId,
                                                 surfaceArea,
                                                 aPosition,
                                                 verticalAtPosition,
                                                 altitudeAtPosition,
                                                 gravityAtPosition)
    assert(isSNumber(planetarySystemId),
           'Argument 1 (planetarySystemId) must be a number:' ..
           type(planetarySystemId))
    assert(isSNumber(bodyId),
           'Argument 2 (bodyId) must be a number:' .. type(bodyId))
    assert(isSNumber(surfaceArea),
           'Argument 3 (surfaceArea) must be a number:' .. type(surfaceArea))
    assert(isTable(aPosition),
           'Argument 4 (aPosition) must be an array or vec3:' ..
           type(aPosition))
    assert(isTable(verticalAtPosition),
           'Argument 5 (verticalAtPosition) must be an array or vec3:' ..
           type(verticalAtPosition))
    assert(isSNumber(altitudeAtPosition),
           'Argument 6 (altitude) must be in meters:' ..
           type(altitudeAtPosition))
    assert(isSNumber(gravityAtPosition),
           'Argument 7 (gravityAtPosition) must be number:' ..
           type(gravityAtPosition))
    local radius   = math.sqrt(surfaceArea/4/math.pi)
    local distance = radius + altitudeAtPosition
    local center   = vec3(aPosition) + distance*vec3(verticalAtPosition)
    local GM       = gravityAtPosition * distance * distance
    return mkBodyParameters(planetarySystemId, bodyId, radius, center, GM)
end
--
-- isMapPosition - check for the presence of the 'MapPosition' fields
-- valueToTest [in]: the value to be checked
-- return: 'true' if all required fields are present in the input value
--
PlanetaryReference.isMapPosition  = isMapPosition
-- PlanetaryReference INSTANCE METHODS:
--
-- getPlanetarySystem - get the planetary system using ID or MapPosition as key
-- overload [in]: either the planetary system ID or a MapPosition that has it.
-- return: instance of 'PlanetarySystem' class or nil on error
--
function PlanetaryReference:getPlanetarySystem(overload)
    --if galaxyAtlas then
        local planetarySystemId = overload
        if isMapPosition(overload) then
            planetarySystemId = overload.systemId
        end
        if type(planetarySystemId) == 'number' then
            local system = self.galaxyAtlas[i]
            if system then
                if getmetatable(nv) ~= PlanetarySystem then
                    system = mkPlanetarySystem(system)
                end
                return system
            end
        end
    --end
    --return nil
end
-- PlanetarySystem INSTANCE METHODS:
--
-- castIntersections - Find the closest body that intersects a "ray cast".
-- origin [in]: the origin of the "ray cast" in world coordinates
-- direction [in]: the direction of the "ray cast" as a 'vec3' instance.
-- sizeCalculator [in]: (default: returns 1.05*radius) Returns size given body.
-- bodyIds[in]: (default: all IDs in system) check only the given IDs.
-- return: The closest body that blocks the cast or 'nil' if none.
--
function PlanetarySystem:castIntersections(origin,
                                           direction,
                                           sizeCalculator,
                                           bodyIds)
    local sizeCalculator = sizeCalculator or
                            function (body) return 1.05*body.radius end
    local candidates = {}
    if bodyIds then
        for _,i in ipairs(bodyIds) do candidates[i] = self[i] end
    else
        bodyIds = {}
        for k,body in pairs(self) do
            table.insert(bodyIds, k)
            candidates[k] = body
        end
    end
    local function compare(b1,b2)
        local v1 = candidates[b1].center - origin
        local v2 = candidates[b2].center - origin
        return v1:len() < v2:len()
    end
    table.sort(bodyIds, compare)
    local dir = direction:normalize()
    for i, id in ipairs(bodyIds) do
        local body   = candidates[id]
        local c_oV3  = body.center - origin
        local radius = sizeCalculator(body)
        local dot    = c_oV3:dot(dir)
        local desc   = dot^2 - (c_oV3:len2() - radius^2)
        if desc >= 0 then
            local root     = math.sqrt(desc)
            local farSide  = dot + root
            local nearSide = dot - root
            if nearSide > 0 then
                return body, farSide, nearSide
            elseif farSide > 0 then
                return body, farSide, nil
            end
        end
    end
    return nil, nil, nil
end
--
-- closestBody - find the closest body to a given set of world coordinates
-- coordinates       [in]: the world coordinates of position in space
-- return: an instance of the BodyParameters object closest to 'coordinates'
--
function PlanetarySystem:closestBody(coordinates)
    assert(type(coordinates) == 'table', 'Invalid coordinates.')
    local minDistance2, body
    local coord = vec3(coordinates)
    for _,params in pairs(self) do
        local distance2 = (params.center - coord):len2()
        if not body or distance2 < minDistance2 then
            body         = params
            minDistance2 = distance2
        end
    end
    return body
end
--
-- convertToBodyIdAndWorldCoordinates - map to body Id and world coordinates
-- overload [in]: an instance of MapPosition or a position string ('::pos...)
-- return: a vec3 instance containing the world coordinates or 'nil' on error.
--
function PlanetarySystem:convertToBodyIdAndWorldCoordinates(overload)
    local mapPosition = overload
    if isString(overload) then
        mapPosition = mkMapPosition(overload)
    end
    if mapPosition.bodyId == 0 then
        return 0, vec3(mapPosition.latitude,
                       mapPosition.longitude,
                       mapPosition.altitude)
    end
    local params = self:getBodyParameters(mapPosition)
    if params then
        return mapPosition.bodyId,
               params:convertToWorldCoordinates(mapPosition)
    end
end
--
-- getBodyParameters - get or create an instance of BodyParameters class
-- overload [in]: either an instance of MapPosition or a body's ID.
-- return: a BodyParameters instance or 'nil' if body ID is not found.
--
function PlanetarySystem:getBodyParameters(overload)
    local bodyId = overload
    if isMapPosition(overload) then
        bodyId = overload.bodyId
    end
    assert(isSNumber(bodyId),
               'Argument 1 (bodyId) must be a number:' .. type(bodyId))
    return self[bodyId]
end
--
-- getPlanetarySystemId - get the planetary system ID for this instance
-- return: the planetary system ID or nil if no planets are in the system.
--
function PlanetarySystem:getPlanetarySystemId()
    local k, v = next(self)
    return v and v.planetarySystemId
end
-- BodyParameters INSTANCE METHODS:
--
-- convertToMapPosition - create an instance of MapPosition from coordinates
-- worldCoordinates [in]: the world coordinates of the map position.
-- return: an instance of MapPosition class
--
function BodyParameters:convertToMapPosition(worldCoordinates)
    assert(isTable(worldCoordinates),
           'Argument 1 (worldCoordinates) must be an array or vec3:' ..
           type(worldCoordinates))
    local worldVec  = vec3(worldCoordinates)
    if self.bodyId == 0 then
        return setmetatable({latitude  = worldVec.x,
                             longitude = worldVec.y,
                             altitude  = worldVec.z,
                             bodyId    = 0,
                             systemId  = self.planetarySystemId}, MapPosition)
    end
    local coords    = worldVec - self.center
    local distance  = coords:len()
    local altitude  = distance - self.radius
    local latitude  = 0
    local longitude = 0
    if not float_eq(distance, 0) then
        local phi = math.atan(coords.y, coords.x)
        longitude = phi >= 0 and phi or (2*math.pi + phi)
        latitude  = math.pi/2 - math.acos(coords.z/distance)
    end
    return setmetatable({latitude  = latitude,
                         longitude = longitude,
                         altitude  = altitude,
                         bodyId    = self.bodyId,
                         systemId  = self.planetarySystemId}, MapPosition)
end
--
-- convertToWorldCoordinates - convert a map position to world coordinates
-- overload [in]: an instance of MapPosition or a position string ('::pos...')
--
function BodyParameters:convertToWorldCoordinates(overload)
    local mapPosition = isString(overload) and
                                           mkMapPosition(overload) or overload
    if mapPosition.bodyId == 0 then -- support deep space map position
        return vec3(mapPosition.latitude,
                    mapPosition.longitude,
                    mapPosition.altitude)
    end
    assert(isMapPosition(mapPosition),
           'Argument 1 (mapPosition) is not an instance of "MapPosition".')
    assert(mapPosition.systemId == self.planetarySystemId,
           'Argument 1 (mapPosition) has a different planetary system ID.')
    assert(mapPosition.bodyId == self.bodyId,
           'Argument 1 (mapPosition) has a different planetary body ID.')
    local xproj = math.cos(mapPosition.latitude)
    return self.center + (self.radius + mapPosition.altitude) *
           vec3(xproj*math.cos(mapPosition.longitude),
                xproj*math.sin(mapPosition.longitude),
                math.sin(mapPosition.latitude))
end
--
-- getAltitude - calculate the altitude of a point given in world coordinates.
-- worldCoordinates [in]: the world coordinates of the point.
-- return: the altitude in meters
--
function BodyParameters:getAltitude(worldCoordinates)
    return (vec3(worldCoordinates) - self.center):len() - self.radius
end
--
-- getDistance - calculate the distance to a point given in world coordinates.
-- worldCoordinates [in]: the world coordinates of the point.
-- return: the distance in meters
--
function BodyParameters:getDistance(worldCoordinates)
    return (vec3(worldCoordinates) - self.center):len()
end
--
-- getGravity - calculate the gravity vector induced by the body.
-- worldCoordinates [in]: the world coordinates of the point.
-- return: the gravity vector in meter/seconds^2
--
function BodyParameters:getGravity(worldCoordinates)
    local radial = self.center - vec3(worldCoordinates) -- directed towards body
    local len2   = radial:len2()
    return (self.GM/len2) * radial/math.sqrt(len2)
end
-- end of module
return setmetatable(PlanetaryReference,
                    { __call = function(_,...)
                                    return mkPlanetaryReference(...)
                               end })
end
function Keplers()
    --[[
  Provides methods for computing orbital information for an object
  Usage:
  Kepler = require('autoconf.custom.kepler')
  alioth = Kepler({ GM=157470826617,
                    bodyId=2,
                    center={x=-8.000,y=-8.000,z=-126303.000},
                    name='Alioth',
                    planetarySystemId=0,
                    radius=126068
                  })
  altitude = 6000
  position = '::pos{0,2,0,0,6000}'
  e, o     = alioth:escapeAndOrbitalSpeed(altitude)
  orbit    = alioth:orbitalParameters(position, {0, o+1, 0})
  print("Eccentricity " .. orbit.eccentricity)
  print("Perihelion " .. orbit.periapsis.altitude)
  print("Max. speed " .. orbit.periapsis.speed)
  print("Circular orbit speed " .. orbit.periapsis.circularOrbitSpeed)
  print("Aphelion "  .. orbit.apoapsis.altitude)
  print("Min. speed " .. orbit.apoapsis.speed)
  print("Orbital period " .. orbit.period)
  --- output:
    Eccentricity 0.0018324307017878
    Perihelion 6000.0
    Max. speed 1092.9462297033
    Circular orbit speed 1091.9462297033
    Aphelion 6484.8994605062
    Min. speed 1088.9480596194
    Orbital period 762.02818214049
  Methods:
    Kepler:escapeAndOrbitalSpeed - for a given celestial body and altitude.
    Kepler:orbitalParameters - for a given massless object and a celestial body.
  Description
  The motion of an object in the vicinity of substantially larger mass is
  in the domain of the "2-body problem". By assuming the object whose motion
  is of interest is of negligable mass simplifies the calculations of:
  the speed to escape the body, the speed of a circular orbit, and the
  parameters defining the orbit of the object (or the lack of orbit as the
  case may be).
  Orbital Parameters:
     periapsis - the closest approach to the planet
      apoapsis - the furthest point from the planet if in orbit (otherwise nil)
  eccentricity - 0 for circular orbits
                <1 for elliptical orbits
                 1 for parabiolic trajectory
                >1 for hyperbolic trajectory
        period - time (in seconds) to complete an orbit
  Also See: planetref.lua
]]--
local vec3       = require('cpml.vec3')
local PlanetRef  = PlanetRef()
local function isString(s) return type(s)   == 'string' end
local function isTable(t)  return type(t)   == 'table'  end
local function float_eq(a,b)
    if a == 0 then return math.abs(b) < 1e-09 end
    if b == 0 then return math.abs(a) < 1e-09 end
    return math.abs(a - b) < math.max(math.abs(a),math.abs(b))*epsilon
end
Kepler = {}
Kepler.__index = Kepler
--
-- escapeAndOrbitalSpeed - speed required to escape and for a circular orbit
-- altitude [in]: the height of the orbit in meters above "sea-level"
-- return: the speed in m/s needed to escape the celestial body and to orbit it.
--
function Kepler:escapeAndOrbitalSpeed(altitude)
    assert(self.body)
    -- P = -GMm/r and KE = mv^2/2 (no lorentz factor used)
    -- mv^2/2 = GMm/r
    -- v^2 = 2GM/r
    -- v = sqrt(2GM/r1)
    local distance = altitude + self.body.radius
    if not float_eq(distance, 0) then
        local orbit = math.sqrt(self.body.GM/distance)
        return math.sqrt(2)*orbit, orbit
    end
    return nil, nil
end
--
-- orbitalParameters: determine the orbital elements for a two-body system.
-- overload [in]: the world coordinates or map coordinates of a massless object.
-- velocity [in]: The velocity of the massless point object in m/s.
-- return: the 6 orbital elements for the massless object.
--
function Kepler:orbitalParameters(overload, velocity)
    assert(self.body)
    assert(isTable(overload) or isString(overload))
    assert(isTable(velocity))
    local pos = (isString(overload) or PlanetRef.isMapPosition(overload)) and
                            self.body:convertToWorldCoordinates(overload) or
                vec3(overload)
    local v   = vec3(velocity)
    local r   = pos - self.body.center
    local v2  = v:len2()
    local d   = r:len()
    local mu  = self.body.GM
    local e   = ((v2 - mu/d)*r - r:dot(v)*v)/mu
    local a   = mu/(2*mu/d - v2)
    local ecc = e:len()
    local dir = e:normalize()
    local pd  = a*(1-ecc)
    local ad  = a*(1+ecc)
    local per = pd*dir + self.body.center
    local apo = ecc <= 1 and -ad*dir + self.body.center or nil
    local trm = math.sqrt(a*mu*(1-ecc*ecc))
    local Period = apo and 2*math.pi*math.sqrt(a^3/mu)
    -- These are great and all, but, I need more.
    local trueAnomaly = math.acos((e:dot(r))/(ecc*d))
    if r:dot(v) < 0 then
        trueAnomaly = -(trueAnomaly - 2*math.pi)
    end
    -- Apparently... cos(EccentricAnomaly) = (cos(trueAnomaly) + eccentricity)/(1 + eccentricity * cos(trueAnomaly))
    local EccentricAnomaly = math.acos((math.cos(trueAnomaly) + ecc)/(1 + ecc * math.cos(trueAnomaly)))
    -- Then.... apparently if this is below 0, we should add 2pi to it
    -- I think also if it's below 0, we're past the apoapsis?
    local timeTau = EccentricAnomaly
    if timeTau < 0 then
        timeTau = timeTau + 2*math.pi
    end
    -- So... time since periapsis...
    -- Is apparently easy if you get mean anomly.  t = M/n where n is mean motion, = 2*pi/Period


    local MeanAnomaly = timeTau - ecc * math.sin(timeTau)
    local TimeSincePeriapsis = MeanAnomaly/(2*math.pi/Period)
    --system.print(MeanAnomaly .. " - " .. TimeSincePeriapsis .. " - " .. Period .. " - " .. EccentricAnomaly .. " - " .. timeTau .. " - " .. trueAnomaly)
    -- Mean anom is 0 at periapsis, positive before it... and positive after it.
    -- I guess this is why I needed to use timeTau and not EccentricAnomaly here

    local TimeToPeriapsis = Period - TimeSincePeriapsis
    local TimeToApoapsis = TimeToPeriapsis + Period/2
    if trueAnomaly - math.pi > 0 then -- TBH I think something's wrong in my formulas because I needed this.
        TimeToPeriapsis = TimeSincePeriapsis
        TimeToApoapsis = TimeToPeriapsis + Period/2
    end
    if TimeToApoapsis > Period then
        TimeToApoapsis = TimeToApoapsis - Period
    end
    return { periapsis       = { position           = per,
                                 speed              = trm/pd,
                                 circularOrbitSpeed = math.sqrt(mu/pd),
                                 altitude           = pd - self.body.radius},
             apoapsis        = apo and
                               { position           = apo,
                                 speed              = trm/ad,
                                 circularOrbitSpeed = math.sqrt(mu/ad),
                                 altitude           = ad - self.body.radius},
             currentVelocity = v,
             currentPosition = pos,
             eccentricity    = ecc,
             period          = Period,
             eccentricAnomaly = EccentricAnomaly,
             meanAnomaly = MeanAnomaly,
             timeToPeriapsis = TimeToPeriapsis,
             timeToApoapsis = TimeToApoapsis
           }
end

local function new(bodyParameters)
    local params = PlanetRef.BodyParameters(bodyParameters.planetarySystemId,
                                            bodyParameters.bodyId,
                                            bodyParameters.radius,
                                            bodyParameters.center,
                                            bodyParameters.GM)
    return setmetatable({body = params}, Kepler)
end
return setmetatable(Kepler, { __call = function(_,...) return new(...) end })
end
function Kinematics()
    --[[
  DualUniverse kinematic equations
  Author: JayleBreak
  Usage (unit.start):
  Kinematics = require('autoconf.custom.kinematics')
  Methods:
   computeAccelerationTime - "relativistic" version of t = (vf - vi)/a
   computeDistanceAndTime - Return distance & time needed to reach final speed.
   computeTravelTime - "relativistic" version of t=(sqrt(2ad+v^2)-v)/a
  Description
  DualUniverse increases the effective mass of constructs as their absolute
  speed increases by using the "lorentz" factor (from relativity) as the scale
  factor.  This results in an upper bound on the absolute speed of constructs
  (excluding "warp" drive) that is set to 30 000 KPH (8 333 MPS). This module
  provides utilities for computing some physical quantities taking this
  scaling into account.
]]--
local Kinematic = {} -- just a namespace
local C       = 30000000/3600
local C2      = C*C
local ITERATIONS = 100 -- iterations over engine "warm-up" period
local function lorentz(v) return 1/math.sqrt(1 - v*v/C2) end
--
-- computeAccelerationTime - "relativistic" version of t = (vf - vi)/a
-- initial      [in]: initial (positive) speed in meters per second.
-- acceleration [in]: constant acceleration until 'finalSpeed' is reached.
-- final        [in]: the speed at the end of the time interval.
-- return: the time in seconds spent in traversing the distance
--
function Kinematic.computeAccelerationTime(initial, acceleration, final)
    -- The low speed limit of following is: t=(vf-vi)/a (from: vf=vi+at)
    local k1 = C*math.asin(initial/C)
    return (C * math.asin(final/C) - k1)/acceleration
end
--
-- computeDistanceAndTime - Return distance & time needed to reach final speed.
-- initial[in]:     Initial speed in meters per second.
-- final[in]:       Final speed in meters per second.
-- restMass[in]:    Mass of the construct at rest in Kg.
-- thrust[in]:      Engine's maximum thrust in Newtons.
-- t50[in]:         (default: 0) Time interval to reach 50% thrust in seconds.
-- brakeThrust[in]: (default: 0) Constant thrust term when braking.
-- return: Distance (in meters), time (in seconds) required for change.
--
function Kinematic.computeDistanceAndTime(initial,
                                          final,
                                          restMass,
                                          thrust,
                                          t50,
                                          brakeThrust)
    -- This function assumes that the applied thrust is colinear with the
    -- velocity. Furthermore, it does not take into account the influence
    -- of gravity, not just in terms of its impact on velocity, but also
    -- its impact on the orientation of thrust relative to velocity.
    -- These factors will introduce (usually) small errors which grow as
    -- the length of the trip increases.
    t50            = t50 or 0
    brakeThrust    = brakeThrust or 0 -- usually zero when accelerating
    local tau0     = lorentz(initial)
    local speedUp  = initial <= final
    local a0       = thrust * (speedUp and 1 or -1)/restMass
    local b0       = -brakeThrust/restMass
    local totA     = a0+b0
    if speedUp and totA <= 0 or not speedUp and totA >= 0 then
        return -1, -1 -- no solution
    end
    local distanceToMax, timeToMax = 0, 0
    -- If, the T50 time is set, then assume engine is at zero thrust and will
    -- reach full thrust in 2*T50 seconds. Thrust curve is given by:
    -- Thrust: F(z)=(a0*(1+sin(z))+2*b0)/2 where z=pi*(t/t50 - 1)/2
    -- Acceleration is given by F(z)/m(z) where m(z) = m/sqrt(1-v^2/c^2)
    -- or v(z)' = (a0*(1+sin(z))+2*b0)*sqrt(1-v(z)^2/c^2)/2
    if a0 ~= 0 and t50 > 0 then
        -- Closed form solution for velocity exists:
        -- v(t) = -c*tan(w)/sqrt(tan(w)^2+1) => w = -asin(v/c)
        -- w=(pi*t*(a0/2+b0)-a0*t50*sin(pi*t/2/t50)+*pi*c*k1)/pi/c
        -- @ t=0, v(0) = vi
        -- pi*c*k1/pi/c = -asin(vi/c)
        -- k1 = asin(vi/c)
        local k1  = math.asin(initial/C)
        local c1  = math.pi*(a0/2+b0)
        local c2  = a0*t50
        local c3  = C*math.pi
        local v = function(t)
            local w  = (c1*t - c2*math.sin(math.pi*t/2/t50) + c3*k1)/c3
            local tan = math.tan(w)
            return C*tan/math.sqrt(tan*tan+1)
        end
        local speedchk = speedUp and function(s) return s >= final end or
                                     function(s) return s <= final end
        timeToMax  = 2*t50
        if speedchk(v(timeToMax)) then
            local lasttime = 0
            while math.abs(timeToMax - lasttime) > 0.5 do
                local t = (timeToMax + lasttime)/2
                if speedchk(v(t)) then
                    timeToMax = t
                else
                    lasttime = t
                end
            end
        end
        -- There is no closed form solution for distance in this case.
        -- Numerically integrate for time t=0 to t=2*T50 (or less)
        local lastv = initial
        local tinc  = timeToMax/ITERATIONS
        for step = 1, ITERATIONS do
            local speed = v(step*tinc)
            distanceToMax = distanceToMax + (speed+lastv)*tinc/2
            lastv = speed
        end
        if timeToMax < 2*t50 then
            return distanceToMax, timeToMax
        end
        initial     = lastv
    end
    -- At full thrust, acceleration only depends on the Lorentz factor:
    -- v(t)' = (F/m(v)) = a*sqrt(1-v(t)^2/c^2) where a = a0+b0
    -- -> v = c*sin((at+k1)/c)
    -- @ t=0, v=vi: k1 = c*asin(vi/c)
    -- -> t = (c*asin(v/c) - k1)/a
    -- x(t)' = c*sin((at+k1)/c)
    -- x = k2 - c^2 cos((at+k1)/c)/a
    -- @ t=0, x=0: k2 = c^2 * cos(k1/c)/a
    local k1       = C*math.asin(initial/C)
    local time     = (C * math.asin(final/C) - k1)/totA
    local k2       = C2 *math.cos(k1/C)/totA
    local distance = k2 - C2 * math.cos((totA*time + k1)/C)/totA
    return distance+distanceToMax, time+timeToMax
end
--
-- computeTravelTime - "relativistic" version of t=(sqrt(2ad+v^2)-v)/a
-- initialSpeed [in]: initial (positive) speed in meters per second
-- acceleration [in]: constant acceleration until 'distance' is traversed
-- distance [in]: the distance traveled in meters
-- return: the time in seconds spent in traversing the distance
--
function Kinematic.computeTravelTime(initial, acceleration, distance)
    -- The low speed limit of following is: t=(sqrt(2ad+v^2)-v)/a
    -- (from: d=vt+at^2/2)
    if distance == 0 then return 0 end
    if acceleration > 0 then
        local k1       = C*math.asin(initial/C)
        local k2       = C2*math.cos(k1/C)/acceleration
        return (C*math.acos(acceleration*(k2 - distance)/C2) - k1)/acceleration
    end
    assert(initial > 0, 'Acceleration and initial speed are both zero.')
    return distance/initial
end
function Kinematic.lorentz(v) return lorentz(v) end
return Kinematic
end
PlanetaryReference = PlanetRef()
galaxyReference = PlanetaryReference(Atlas())
Kinematic = Kinematics()
Kep = Keplers()
function getDistanceDisplayString(distance)
    local su = distance > 100000
    local result = ""
    if su then
        -- Convert to SU
        result = round(distance/1000/200,1) .. " SU"
    else
        -- Convert to KM
        result = round(distance/1000,1) .. " KM"
    end

    return result
end

PlanetaryReference = PlanetRef()
galaxyReference = PlanetaryReference(Atlas())

MapScreenButtons = {}
MapScreenMouseX = 0
MapScreenMouseY = 0
MapScreenMouseDown = false
MapScreenButtonSelected = 0
local worldPos = vec3(core.getConstructWorldPos())
local locX = (worldPos.x/400000)
local locY = (worldPos.y/400000)*(-1)
local destX = 0
local destY = 0
local sudistance = 0
local loc = vec3(core.getConstructWorldPos())
local ion = galaxyReference[0][120]  ---uses Atlas functions
local thades = vec3(29165536.000, 10865536.000, 65536.000)
local sinnen = vec3(58665536.000, 29665536.000, 58165536.000)
local alioth = galaxyReference[0][2]    ---uses Atlas functions
local madis = vec3(17465536.000, 22665536.000, -34464.000)
local jago = vec3(-94134464.000, 12765536.000, -3634464.000)
local symeon = vec3(14165536.000, -85634464.000, -934464.000)
local lacobus = vec3(98865536.000, -13534464.000, -934464.000)
local teoma = vec3(80865536.000, 54665536.000, -934464.000)
local feli = vec3(-43534464.000, 22565536.000, -48934464.000)
local talemai = vec3(-13234464.000, 55765536.000, 465536.000)
local sicari = vec3(52765536.000, 27165536.000, 52065536.000)
local distion = math.floor(ion:getDistance(loc)/200000)   ---uses getDistance functions----
local distthades = string.format("%.2f", math.sqrt((loc.x-thades.x)^2+(loc.y-thades.y)^2+(loc.z-thades.z)^2)/200000)
local distalioth = math.floor(alioth:getDistance(loc)/200000)   ---uses getDistance functions----
local distmadis = string.format("%.2f", math.sqrt((loc.x-madis.x)^2+(loc.y-madis.y)^2+(loc.z-madis.z)^2)/200000)
local distjago = string.format("%.2f", math.sqrt((loc.x-jago.x)^2+(loc.y-jago.y)^2+(loc.z-jago.z)^2)/200000)
local distlacobus = string.format("%.2f", math.sqrt((loc.x-lacobus.x)^2+(loc.y-lacobus.y)^2+(loc.z-lacobus.z)^2)/200000)
local distteoma = string.format("%.2f", math.sqrt((loc.x-teoma.x)^2+(loc.y-teoma.y)^2+(loc.z-teoma.z)^2)/200000)
local distsymeon = string.format("%.2f", math.sqrt((loc.x-symeon.x)^2+(loc.y-symeon.y)^2+(loc.z-symeon.z)^2)/200000)
local distfeli = string.format("%.2f", math.sqrt((loc.x-feli.x)^2+(loc.y-feli.y)^2+(loc.z-feli.z)^2)/200000)
local distsinnen = string.format("%.2f", math.sqrt((loc.x-sinnen.x)^2+(loc.y-sinnen.y)^2+(loc.z-sinnen.z)^2)/200000)
local disttalemai = string.format("%.2f", math.sqrt((loc.x-talemai.x)^2+(loc.y-talemai.y)^2+(loc.z-talemai.z)^2)/200000)
local distsicari = string.format("%.2f", math.sqrt((loc.x-sicari.x)^2+(loc.y-sicari.y)^2+(loc.z-sicari.z)^2)/200000)



      for i = 1,1 do
   local button = {id = ("b"..1), enabled=true, td="<td>", top=2/100, bottom=13/100, left=1/100, right=28/100}
    table.insert(MapScreenButtons, button)
end
  for i = 2,2 do
   local button = {id = ("b"..2), enabled=true, td="<td>", top=15/100, bottom=26/100, left=1/100, right=30/100}
    table.insert(MapScreenButtons, button)
end
    for i = 3,3 do
    local button = {id = ("b"..3), enabled=true, td="<td>", top=27/100, bottom=38/100, left=1/100, right=28/100}
      table.insert(MapScreenButtons, button)
end
    for i = 4,4 do
    local button = {id = ("b"..4), enabled=true, td="<td>", top=39/100, bottom=50/100, left=1/100, right=28/100}
      table.insert(MapScreenButtons, button)
end
    for i = 5,5 do
    local button = {id = ("b"..5), enabled=true, td="<td>", top=51/100, bottom=62/100, left=1/100, right=28/100}
      table.insert(MapScreenButtons, button)
end
    for i = 6,6 do
    local button = {id = ("b"..6), enabled=true, td="<td>", top=64/100, bottom=75/100, left=1/100, right=28/100}
      table.insert(MapScreenButtons, button)
end
    for i = 7,7 do
    local button = {id = ("b"..7), enabled=true, td="<td>", top=2/100, bottom=13/100, left=75/100, right=100/100}
      table.insert(MapScreenButtons, button)
end
    for i = 8,8 do
    local button = {id = ("b"..8), enabled=true, td="<td>", top=15/100, bottom=26/100, left=75/100, right=100/100}
      table.insert(MapScreenButtons, button)
end
    for i = 9,9 do
    local button = {id = ("b"..9), enabled=true, td="<td>", top=27/100, bottom=38/100, left=75/100, right=100/100}
      table.insert(MapScreenButtons, button)
end
    for i = 10,10 do
    local button = {id = ("b"..10), enabled=true, td="<td>", top=39/100, bottom=50/100, left=75/100, right=100/100}
      table.insert(MapScreenButtons, button)
end
    for i = 11,11 do
    local button = {id = ("b"..11), enabled=true, td="<td>", top=51/100, bottom=62/100, left=75/100, right=100/100}
      table.insert(MapScreenButtons, button)
end
    for i = 12,12 do
    local button = {id = ("b"..12), enabled=true, td="<td>", top=64/100, bottom=75/100, left=75/100, right=100/100}
      table.insert(MapScreenButtons, button)
end
    for i = 13,13 do
    local button = {id = ("b"..13), enabled=true, td="<td>", top=90/100, bottom=100/100, left=1/100, right=18/100}
    table.insert(MapScreenButtons, button)
end
function evaluateButtons()
  local selected = 0

  if #MapScreenButtons >= 1 then
 -- Set button styles
       for i, button in ipairs(MapScreenButtons) do
            if button.left < MapScreenMouseX and MapScreenMouseX < button.right and button.top < MapScreenMouseY and MapScreenMouseY < button.bottom then
                if MapScreenMouseDown and MapScreenButtonSelected == i then
                end
                selected = i
            end
            if not button.enabled then
            end

        end
  end
  return selected
end

function onButtonDown(buttonNo)
  local button = MapScreenButtons[buttonNo]
  if not button or not button.enabled then
	return
  end
end
function onButtonUp(buttonNo)
  local button = MapScreenButtons[buttonNo]
  if not button or not button.enabled then
    return
  end
function onClick(buttonNo)
  local button = MapScreenButtons[buttonNo]
  if not button or not button.enabled then
    return
  end
end

local selection = 0

  if buttonNo == 1 then
destX = 0
destY = 0
selection = 1
sudistance = distalioth
  elseif buttonNo == 2 then
destX = 43
destY = -56
sudistance = distmadis
selection = 2
  elseif buttonNo == 3 then
destX = 73
destY = -27
selection = 3
sudistance = distthades
  elseif buttonNo == 4 then
destX = -33
destY = -139
selection = 4
sudistance = disttalemai
  elseif buttonNo == 5 then
destX = -109
destY = -56
selection = 5
sudistance = distfeli
  elseif buttonNo == 6 then
destX = 131
destY = -68
selection = 6
sudistance = distsicari
  elseif buttonNo == 7 then
destX = 35
destY = 214
selection = 7
sudistance = distsymeon
  elseif buttonNo == 8 then
destX = 146
destY = -74
selection = 8
sudistance = distsinnen
  elseif buttonNo == 9 then
destX = -235
destY = -32
selection = 9
sudistance = distjago
  elseif buttonNo == 10 then
destX = 202
destY = -137
selection = 10
sudistance = distteoma
  elseif buttonNo == 11 then
destX = 7
destY = 247
selection = 11
sudistance = distion
  elseif buttonNo == 12 then
destX = 247
destY = 34
selection = 12
sudistance = distlacobus
  elseif buttonNo == 13 then
  unit.exit()
  end
end

function updateScreen()

loc = vec3(core.getConstructWorldPos())

local shipVelocity = vec3(core.getVelocity()):len() * 3.6
local shipAcceleration = vec3(core.getVelocity()):len() * 3.6
local time_to_distance = 0

if selection == 1 then
    alioth = galaxyReference[0][2]    ---uses Atlas functions
    distalioth = math.floor(alioth:getDistance(loc)/200000)   ---uses getDistance functions----
    time_to_distance = distalioth * 200 / shipVelocity
elseif selection == 2 then
    madis = vec3(17465536.000, 22665536.000, -34464.000)
    distmadis = string.format("%.2f", math.sqrt((loc.x-madis.x)^2+(loc.y-madis.y)^2+(loc.z-madis.z)^2)/200000)
    time_to_distance = distmadis * 200 / shipVelocity
elseif selection == 3 then
    thades = vec3(29165536.000, 10865536.000, 65536.000)
    distthades = string.format("%.2f", math.sqrt((loc.x-thades.x)^2+(loc.y-thades.y)^2+(loc.z-thades.z)^2)/200000)
    time_to_distance = distthades * 200 / shipVelocity
elseif selection == 4 then
    talemai = vec3(-13234464.000, 55765536.000, 465536.000)
    disttalemai = string.format("%.2f", math.sqrt((loc.x-talemai.x)^2+(loc.y-talemai.y)^2+(loc.z-talemai.z)^2)/200000)
    time_to_distance = disttalemai * 200 / shipVelocity
elseif selection == 5 then
    feli = vec3(-43534464.000, 22565536.000, -48934464.000)
    distfeli = string.format("%.2f", math.sqrt((loc.x-feli.x)^2+(loc.y-feli.y)^2+(loc.z-feli.z)^2)/200000)
    time_to_distance = distfeli * 200 / shipVelocity
elseif selection == 6 then
    sicari = vec3(52765536.000, 27165536.000, 52065536.000)
    distsicari = string.format("%.2f", math.sqrt((loc.x-sicari.x)^2+(loc.y-sicari.y)^2+(loc.z-sicari.z)^2)/200000)
    time_to_distance = distsicari * 200 / shipVelocity
elseif selection == 7 then
    symeon = vec3(14165536.000, -85634464.000, -934464.000)
    distsymeon = string.format("%.2f", math.sqrt((loc.x-symeon.x)^2+(loc.y-symeon.y)^2+(loc.z-symeon.z)^2)/200000)
    time_to_distance = distsymeon * 200 / shipVelocity
elseif selection == 8 then
    sinnen = vec3(58665536.000, 29665536.000, 58165536.000)
    distsinnen = string.format("%.2f", math.sqrt((loc.x-sinnen.x)^2+(loc.y-sinnen.y)^2+(loc.z-sinnen.z)^2)/200000)
    time_to_distance = distsinnen * 200 / shipVelocity
elseif selection == 9 then
    jago = vec3(-94134464.000, 12765536.000, -3634464.000)
    distjago = string.format("%.2f", math.sqrt((loc.x-jago.x)^2+(loc.y-jago.y)^2+(loc.z-jago.z)^2)/200000)
    time_to_distance = distjago * 200 / shipVelocity
elseif selection == 10 then
    teoma = vec3(80865536.000, 54665536.000, -934464.000)
    distteoma = string.format("%.2f", math.sqrt((loc.x-teoma.x)^2+(loc.y-teoma.y)^2+(loc.z-teoma.z)^2)/200000)
    time_to_distance = distteoma * 200 / shipVelocity
elseif selection == 11 then
    ion = galaxyReference[0][120]  ---uses Atlas functions
    distion = math.floor(ion:getDistance(loc)/200000)   ---uses getDistance functions----
    time_to_distance = distion * 200 / shipVelocity
elseif selection == 12 then
    lacobus = vec3(98865536.000, -13534464.000, -934464.000)
    distlacobus = string.format("%.2f", math.sqrt((loc.x-lacobus.x)^2+(loc.y-lacobus.y)^2+(loc.z-lacobus.z)^2)/200000)
    time_to_distance = distlacobus * 200 / shipVelocity
else
    time_to_distance = sudistance * 200 / shipVelocity
end

warpmath = math.floor(math.floor(core.getConstructMass()/ 1000)  * sudistance * 0.00025)

html= ([[
<svg width="1024" height="1024" viewBox="0 0 1024 1640"><circle cx="500" cy="500" r="400" stroke="darkgreen" stroke-width="3" transform=""></circle><circle cx="500" cy="500" r="350" stroke="#FFF" stroke-width="3" transform="" stroke-opacity="0.2"></circle><circle cx="500" cy="500" r="300" stroke="#FFF" stroke-width="3" transform=""></circle><circle cx="500" cy="500" r="250" stroke="#FFF" stroke-width="3" transform="" stroke-opacity="0.2"></circle><circle cx="500" cy="500" r="200" stroke="#FFF" stroke-width="3" transform=""></circle><circle cx="500" cy="500" r="150" stroke="#FFF" stroke-width="3" transform="" stroke-opacity="0.2"></circle><circle cx="500" cy="500" r="100" stroke="lightblue" stroke-width="3" transform=""></circle><circle cx="500" cy="500" r="50" stroke="lightblue" stroke-width="3" transform="" stroke-opacity="0.2"></circle><circle cx="500" cy="500" r="20" stroke="Orange" stroke-width="2" transform=""></circle><text x="510" y="510" fill="Yellow">Helios</text><circle cx="-0.00" cy="0" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="-0.00" y="0" transform="translate(500,480)" fill="white" font-size="20">Alioth</text><circle cx="7.16" cy="247.59" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="7.16" y="247.59" transform="translate(480,480)" fill="white" font-size="20">Ion</text><circle cx="35.41" cy="214.09" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="35.41" y="214.09" transform="translate(500,480)" fill="white" font-size="20">Symeon</text><circle cx="-33.09" cy="-139.41" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="-33.09" y="-139.41" transform="translate(500,480)" fill="white" font-size="20">Talemai</text><circle cx="202.16" cy="-136.66" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="202.16" y="-136.66" transform="translate(500,480)" fill="white" font-size="20">Teoma</text><circle cx="247.16" cy="33.84" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="247.16" y="33.84" transform="translate(500,480)" fill="white" font-size="20">Lacobus</text><circle cx="-108.84" cy="-56.41" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="-108.84" y="-56.41" transform="translate(500,480)" fill="white" font-size="20">Feli</text><circle cx="72.91" cy="-27.16" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="72.91" y="-27.16" transform="translate(500,485)" fill="white" font-size="20">Thades</text><circle cx="43.66" cy="-56.66" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="43.66" y="-56.66" transform="translate(500,480)" fill="white" font-size="20">Madis</text><circle cx="-235.34" cy="-31.91" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="-235.34" y="-31.91" transform="translate(500,480)" fill="white" font-size="20">Jago</text><circle cx="131.91" cy="-67.91" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="131.91" y="-67.91" transform="translate(475,480)" fill="white" font-size="20">Sicari</text><circle cx="146.66" cy="-74.16" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="146.66" y="-74.16" transform="translate(515,480)" fill="white" font-size="20">Sinnen</text>
<line stroke-linecap="undefined" stroke-linejoin="undefined" id="svg_1" y2="]]..destY..[[" x2="]]..destX..[[" y1="]]..locY..[[" x1="]]..locX..[[" transform="translate(500,500)" stroke-width="5" stroke="#ff0000" fill="none"/>
<circle cx="]]..locX..[[" cy="]]..locY..[[" r="3" stroke="black" stroke-width="1" fill="limegreen" transform="translate(500,500)"></circle>
<text x="]]..locX..[[" y="]]..locY..[[" transform="translate(500,500)"
fill="limegreen" font-size= "4.5vh" font-weight= "bold">//SHIP POSITION</text>
</svg>
<svg width="1024" height="612" xmlns="http://www.w3.org/2000/svg">>
 <g>
  <title>Layer 1</title>
  <g id="svg_24">
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_8" y="70" x="55" stroke-width="0" fill="Yellow">Alioth  :]]..distalioth..[[ SU</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_14" y="170" x="55" stroke-width="0" fill="Yellow">Madis  :]]..distmadis..[[ SU</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_17" y="270" x="55" stroke-width="0" fill="Yellow">Thades  :]]..distthades..[[ SU</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_20" y="370" x="55" stroke-width="0" fill="Yellow">Talemai  :]]..disttalemai..[[ SU</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_23" y="470" x="55" stroke-width="0" fill="Yellow">Feli  :]]..distfeli..[[ SU</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_26" y="570" x="55" stroke-width="0" fill="Yellow">Sicari  :]]..distsicari..[[ SU</text>
   <g id="svg_12">
    <rect rx="10" id="svg_1" height="50" width="250" y="30" x="15" stroke-width="10" stroke="#FFF" fill="none"/>
    <rect rx="10" id="svg_3" height="50" width="250" y="105" x="15" stroke-width="10" stroke="#FFF" fill="none"/>
    <rect rx="10" id="svg_7" height="50" width="250" y="180" x="15" stroke-width="10" stroke="#FFF" fill="none"/>
    <rect rx="10" id="svg_9" height="50" width="250" y="255" x="15" stroke-width="10" stroke="#FFF" fill="none"/>
    <rect rx="10" id="svg_10" height="50" width="250" y="330" x="15" stroke-width="10" stroke="#FFF" fill="none"/>
    <rect rx="10" id="svg_11" height="50" width="250" y="405" x="15" stroke-width="10" stroke="#FFF" fill="none"/>
   </g>
  </g>
  <g id="svg_40">
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_25" y="70" x="997.163642" stroke-width="0" fill="Yellow">Symeon  :]]..distsymeon..[[ SU</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_27" y="170" x="997.163642" stroke-width="0" fill="Yellow">Sinnen  :]]..distsinnen..[[ SU</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_28" y="270" x="997.163642" stroke-width="0" fill="Yellow">Jago  :]]..distjago..[[ SU</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_30" y="370" x="997.163642" stroke-width="0" fill="Yellow">Teoma  :]]..distteoma..[[ SU</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_31" y="470" x="997.163642" stroke-width="0" fill="Yellow">Ion  :]]..distion..[[ SU</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_32" y="570" x="997.163642" stroke-width="0" fill="Yellow">Lacobus  :]]..distlacobus..[[ SU</text>
   <g id="svg_39">
    <rect rx="10" id="svg_33" height="50" width="250" y="30" x="760" stroke-width="10" stroke="#FFF" fill="none"/>
    <rect rx="10" id="svg_34" height="50" width="250" y="105" x="760" stroke-width="10" stroke="#FFF" fill="none"/>
    <rect rx="10" id="svg_35" height="50" width="250" y="180" x="760" stroke-width="10" stroke="#FFF" fill="none"/>
    <rect rx="10" id="svg_36" height="50" width="250" y="255" x="760" stroke-width="10" stroke="#FFF" fill="none"/>
    <rect rx="10" id="svg_37" height="50" width="250" y="330" x="760" stroke-width="10" stroke="#FFF" fill="none"/>
    <rect rx="10" id="svg_38" height="50" width="250" y="405" x="760" stroke-width="10" stroke="#FFF" fill="none"/>
   </g>
  </g>
 </g>
  <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="30" id="svg_32" y="700" x="20" stroke-width="0" fill="LightBlue">Est. Warp Cost: ]]..warpmath..[[</text>
  <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="30" id="svg_32" y="750" x="20" stroke-width="0" fill="LightBlue">Construct Weight: ]]..math.floor(core.getConstructMass()/ 1000)..[[ tons</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="30" id="svg_32" y="700" x="500" stroke-width="0" fill="LightBlue">TTD: ]]..time_to_distance..[[ hrs</text>
   <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="30" id="svg_32" y="750" x="500" stroke-width="0" fill="LightBlue">Vel: ]]..shipVelocity..[[ km/h</text>
</svg>

    ]])

screen.setHTML(html)
screen2.setHTML(html)
end
unit.setTimer("spacemap",.08)

 

Link to comment
Share on other sites

i have an issue when changing the destination on the map. when i use an empty dynamic core all works well. but when i use it on an existing ship i receive "invalid construct". any ideas? thanks in advance :)

 

 

EDIT: Relog did the magic :P

Link to comment
Share on other sites

Thanks for the work you've done. Good job!

It would be great to upgrade the script, so that it could find the most appropriate route, because the game's map is not dimensional and there is a 500 SU restriction for a warp.
Sometimes it is easier/more quickly to get to the target through other planets.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...