Jump to content

Space Map with Warp Cell Calculator


RONinja

Recommended Posts

I am having an issue with the script, getting an error. The issue would be easy enough to find, except when I paste it from your original post, it's all in 1 line. 

 

Error:   '}' expected near ':'

 

There's 341 of those characters in the whole script...

 

 

EDIT: Nevermind. Helps if I follow your video you linked. Thanks.

Link to comment
Share on other sites

  • 3 weeks later...

Just update your unit > start() with this code for a better looking map:

 

image.thumb.png.65da9fc3601866fd019e62cb2c672870.png

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)
selection = 0


      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
selection = 1
sudistance = distalioth
  elseif buttonNo == 2 then
destX = 43
destY = -56
selection = 2
sudistance = distmadis
  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


local floor = math.floor
local concat = table.concat

local secondsInMinute = 60
local secondsInHour = secondsInMinute * 60
local secondsInDay = secondsInHour * 24
local secondsInYear = 365.2419 * secondsInDay

local minTotalSecondsToShowOnlyYears = secondsInYear * 10

---@param totalSeconds number
---@param maxComponents nil|number
local function formatTimeWithUnits (totalSeconds, maxComponents)
  maxComponents = maxComponents or 2

  local buffer = {}

  if totalSeconds < 0 then
    buffer[#buffer + 1] = "-"
    totalSeconds = -totalSeconds
    maxComponents = maxComponents + 1
  end

  local showOnlyYears = totalSeconds > minTotalSecondsToShowOnlyYears

  local years = floor(totalSeconds / secondsInYear)
  if years > 0 then buffer[#buffer + 1] = years .. "y" end

  if #buffer < maxComponents and not showOnlyYears then
    local days = floor(totalSeconds % secondsInYear / secondsInDay)
    if days > 0 then buffer[#buffer + 1] = days .. "d" end
  end

  if #buffer < maxComponents and not showOnlyYears then
    local hours = floor(totalSeconds % secondsInDay / secondsInHour)
    if hours > 0 then buffer[#buffer + 1] = hours .. "h" end
  end

  if #buffer < maxComponents and not showOnlyYears then
    local minutes = floor(totalSeconds % secondsInHour / secondsInMinute)
    if minutes > 0 then buffer[#buffer + 1] = minutes .. "m" end
  end

  if #buffer < maxComponents and not showOnlyYears then
    local seconds = floor(totalSeconds % secondsInMinute)
    if seconds > 0 then buffer[#buffer + 1] = seconds .. "s" end
  end

  if #buffer == 0 then return "0s" end

  return concat(buffer, " ")

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
local display_selection = ""

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
    display_selection = "alioth"
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
    display_selection = "madis"
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
    display_selection = "thades"
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
    display_selection = "talemai"
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
    display_selection = "feli"
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
    display_selection = "sicari"
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
    display_selection = "symeon"
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
    display_selection = "sinnen"
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
    display_selection = "jago"
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
    display_selection = "teoma"
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
    display_selection = "ion"
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
    display_selection = "lacobus"
else
    time_to_distance = sudistance * 200 / shipVelocity
    display_selection = "none"
end
shipVelocity = string.format("%.2f", shipVelocity)
warpmath = math.floor(math.floor(core.getConstructMass()/ 1000)  * sudistance * 0.00025)

time_to_distance_min = time_to_distance * 60
time_to_distance_min_sec = time_to_distance_min * 60

time_to_distance = formatTimeWithUnits (time_to_distance_min_sec, 3)

html= ([[
<svg class="bootstrap" viewBox="0 0 1000 970" style="width:100%; height:100%; margin-top:-5em;">

<circle cx="500" cy="500" r="400" stroke="#5a79c8" stroke-width="5" transform=""></circle>
<circle cx="500" cy="500" r="350" stroke="#5a79c8" stroke-width="5" transform="" stroke-opacity="0.2"></circle>
<circle cx="500" cy="500" r="300" stroke="#5a79c8" stroke-width="5" transform=""></circle>
<circle cx="500" cy="500" r="250" stroke="#5a79c8" stroke-width="5" transform="" stroke-opacity="0.2"></circle>
<circle cx="500" cy="500" r="200" stroke="#5a79c8" stroke-width="5" transform=""></circle>
<circle cx="500" cy="500" r="150" stroke="#5a79c8" stroke-width="5" transform="" stroke-opacity="0.2"></circle>
<circle cx="500" cy="500" r="100" stroke="#5a79c8" fill="#5fb7cf" stroke-width="2" transform="" fill-opacity="0.2"></circle>
<circle cx="500" cy="500" r="50" stroke="#5a79c8" stroke-width="2" transform="" stroke-opacity="0.2"></circle>

<circle cx="500" cy="500" r="20" stroke="#ffdf91" stroke-width="1.5" transform=""></circle>
<text x="525" y="504" fill="#ffdf91"font-size="15">Helios</text>

<circle cx="-0.00" cy="0" r="10" stroke="black" stroke-width="1" fill="#61a7ff" transform="translate(500,500)"></circle>
<text x="-95.00" y="25" transform="translate(500,480)" fill="#61a7ff" font-size="20">Alioth</text>

<circle cx="5.41" cy="244.09" r="10" stroke="black" stroke-width="1" fill="#e1e1e1" transform="translate(500,500)"></circle>
<text x="-30" y="270" transform="translate(480,480)" fill="#e1e1e1" font-size="20">Ion</text>

<circle cx="35.41" cy="214.09" r="10" stroke="black" stroke-width="1" fill="#7c90ff" transform="translate(500,500)"></circle>
<text x="-62" y="239" transform="translate(500,480)" fill="#7c90ff" font-size="20">Symeon</text>

<circle cx="202.16" cy="-136.66" r="10" stroke="black" stroke-width="1" fill="#bddeff" transform="translate(500,500)"></circle>
<text x="112" y="-109" transform="translate(500,480)" fill="#bddeff" font-size="20">Teoma</text>

<circle cx="-33.09" cy="-139.41" r="10" stroke="black" stroke-width="1" fill="#cfffdd" transform="translate(500,500)"></circle>
<text x="-139" y="-112" transform="translate(500,480)" fill="#cfffdd" font-size="20">Talemai</text>

<circle cx="-108.84" cy="-56.41" r="10" stroke="black" stroke-width="1" fill="#c6a668" transform="translate(500,500)"></circle>
<text x="-164" y="-30" transform="translate(500,480)" fill="#c6a668" font-size="20">Feli</text>

<circle cx="-235.34" cy="-31.91" r="10" stroke="black" stroke-width="1" fill="#8dc0e5" transform="translate(500,500)"></circle>
<text x="-297" y="-5" transform="translate(500,480)" fill="#8dc0e5" font-size="20">Jago</text>

<circle cx="43.66" cy="-56.66" r="10" stroke="black" stroke-width="1" fill="#8a9fff" transform="translate(500,500)"></circle>
<text x="-38" y="-28" transform="translate(500,480)" fill="#8a9fff" font-size="20">Madis</text>

<circle cx="72.91" cy="-27.16" r="10" stroke="black" stroke-width="1" fill="#ffaf6a" transform="translate(500,500)"></circle>
<text x="88" y="-7" transform="translate(500,485)" fill="#ffaf6a" font-size="20">Thades</text>

<circle cx="247.16" cy="33.84" r="10" stroke="black" stroke-width="1" fill="#cacaca" transform="translate(500,500)"></circle>
<text x="263" y="62" transform="translate(500,480)" fill="#cacaca" font-size="20">Lacobus</text>

<circle cx="-108.84" cy="-56.41" r="10" stroke="black" stroke-width="1" fill="#c6a668" transform="translate(500,500)"></circle>
<text x="-165.84" y="-30.41" transform="translate(500,480)" fill="#c6a668" font-size="20">Feli</text>

<circle cx="131.91" cy="-67.91" r="10" stroke="black" stroke-width="1" fill="#ffac71" transform="translate(500,500)"></circle>
<text x="81" y="-40" transform="translate(475,480)" fill="#ffac71" font-size="20">Sicari</text>

<circle cx="146.66" cy="-74.16" r="10" stroke="black" stroke-width="1" fill="#ffa775" transform="translate(500,500)"></circle>
<text x="146" y="-46" transform="translate(515,480)" fill="#ffa775" 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="#ffb400" 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">&nbsp;&nbsp;&nbsp;&nbsp;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_12">
    <rect rx="10" id="svg_1" height="50" width="230" y="30" x="15" stroke-width="4" stroke="#aee4ed" fill="#5fb7cf"/>
    <rect rx="10" id="svg_3" height="50" width="230" y="105" x="15" stroke-width="4" stroke="#aee4ed" fill="#5fb7cf"/>
    <rect rx="10" id="svg_7" height="50" width="230" y="180" x="15" stroke-width="4" stroke="#aee4ed" fill="#5fb7cf"/>
    <rect rx="10" id="svg_9" height="50" width="230" y="255" x="15" stroke-width="4" stroke="#aee4ed" fill="#5fb7cf"/>
    <rect rx="10" id="svg_10" height="50" width="230" y="330" x="15" stroke-width="4" stroke="#aee4ed" fill="#5fb7cf"/>
    <rect rx="10" id="svg_11" height="50" width="230" y="405" x="15" stroke-width="4" stroke="#aee4ed" fill="#5fb7cf"/>
   </g>
  <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="30" id="svg_8" y="75" x="40" stroke-width="0" fill="black">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="30" id="svg_14" y="175" x="40" stroke-width="0" fill="black">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="30" id="svg_17" y="275" x="40" stroke-width="0" fill="black">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="30" id="svg_20" y="372" x="40" stroke-width="0" fill="black">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="30" id="svg_23" y="469" x="40" stroke-width="0" fill="black">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="30" id="svg_26" y="568" x="40" stroke-width="0" fill="black">Sicari  :]]..distsicari..[[ SU</text>
  </g>
  <g id="svg_40">
   <g id="svg_39">
    <rect rx="10" id="svg_33" height="50" width="230" y="30" x="780" stroke-width="4" stroke="#aee4ed" fill="#5fb7cf"/>
    <rect rx="10" id="svg_34" height="50" width="230" y="105" x="780" stroke-width="4" stroke="#aee4ed" fill="#5fb7cf"/>
    <rect rx="10" id="svg_35" height="50" width="230" y="180" x="780" stroke-width="4" stroke="#aee4ed" fill="#5fb7cf"/>
    <rect rx="10" id="svg_36" height="50" width="230" y="255" x="780" stroke-width="4" stroke="#aee4ed" fill="#5fb7cf"/>
    <rect rx="10" id="svg_37" height="50" width="230" y="330" x="780" stroke-width="4" stroke="#aee4ed" fill="#5fb7cf"/>
    <rect rx="10" id="svg_38" height="50" width="230" y="405" x="780" stroke-width="4" stroke="#aee4ed" fill="#5fb7cf"/>
    </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_25" y="75" x="1007.163642" stroke-width="0" fill="black">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="30" id="svg_27" y="175" x="1007.163642" stroke-width="0" fill="black">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="30" id="svg_28" y="275" x="1007.163642" stroke-width="0" fill="black">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="30" id="svg_30" y="372" x="1007.163642" stroke-width="0" fill="black">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="30" id="svg_31" y="469" x="1007.163642" stroke-width="0" fill="black">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="30" id="svg_32" y="568" x="1007.163642" stroke-width="0" fill="black">Lacobus  :]]..distlacobus..[[ SU</text>

  </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="#976eb0">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="#976eb0">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="#976eb0">TTD: ]]..time_to_distance..[[ </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="#976eb0">VELOC: ]]..shipVelocity..[[ km/h</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="900" stroke-width="0" fill="#976eb0">DEST: ]]..display_selection..[[ </text>

</svg>

    ]])

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


 

Link to comment
Share on other sites

  • 2 weeks later...
On 11/6/2020 at 11:44 AM, Resource said:

Just update your unit > start() with this code for a better looking map:

 

image.thumb.png.65da9fc3601866fd019e62cb2c672870.png


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)
selection = 0


      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
selection = 1
sudistance = distalioth
  elseif buttonNo == 2 then
destX = 43
destY = -56
selection = 2
sudistance = distmadis
  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


local floor = math.floor
local concat = table.concat

local secondsInMinute = 60
local secondsInHour = secondsInMinute * 60
local secondsInDay = secondsInHour * 24
local secondsInYear = 365.2419 * secondsInDay

local minTotalSecondsToShowOnlyYears = secondsInYear * 10

---@param totalSeconds number
---@param maxComponents nil|number
local function formatTimeWithUnits (totalSeconds, maxComponents)
  maxComponents = maxComponents or 2

  local buffer = {}

  if totalSeconds < 0 then
    buffer[#buffer + 1] = "-"
    totalSeconds = -totalSeconds
    maxComponents = maxComponents + 1
  end

  local showOnlyYears = totalSeconds > minTotalSecondsToShowOnlyYears

  local years = floor(totalSeconds / secondsInYear)
  if years > 0 then buffer[#buffer + 1] = years .. "y" end

  if #buffer < maxComponents and not showOnlyYears then
    local days = floor(totalSeconds % secondsInYear / secondsInDay)
    if days > 0 then buffer[#buffer + 1] = days .. "d" end
  end

  if #buffer < maxComponents and not showOnlyYears then
    local hours = floor(totalSeconds % secondsInDay / secondsInHour)
    if hours > 0 then buffer[#buffer + 1] = hours .. "h" end
  end

  if #buffer < maxComponents and not showOnlyYears then
    local minutes = floor(totalSeconds % secondsInHour / secondsInMinute)
    if minutes > 0 then buffer[#buffer + 1] = minutes .. "m" end
  end

  if #buffer < maxComponents and not showOnlyYears then
    local seconds = floor(totalSeconds % secondsInMinute)
    if seconds > 0 then buffer[#buffer + 1] = seconds .. "s" end
  end

  if #buffer == 0 then return "0s" end

  return concat(buffer, " ")

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
local display_selection = ""

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
    display_selection = "alioth"
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
    display_selection = "madis"
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
    display_selection = "thades"
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
    display_selection = "talemai"
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
    display_selection = "feli"
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
    display_selection = "sicari"
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
    display_selection = "symeon"
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
    display_selection = "sinnen"
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
    display_selection = "jago"
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
    display_selection = "teoma"
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
    display_selection = "ion"
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
    display_selection = "lacobus"
else
    time_to_distance = sudistance * 200 / shipVelocity
    display_selection = "none"
end
shipVelocity = string.format("%.2f", shipVelocity)
warpmath = math.floor(math.floor(core.getConstructMass()/ 1000)  * sudistance * 0.00025)

time_to_distance_min = time_to_distance * 60
time_to_distance_min_sec = time_to_distance_min * 60

time_to_distance = formatTimeWithUnits (time_to_distance_min_sec, 3)

html= ([[
<svg class="bootstrap" viewBox="0 0 1000 970" style="width:100%; height:100%; margin-top:-5em;">

<circle cx="500" cy="500" r="400" stroke="#5a79c8" stroke-width="5" transform=""></circle>
<circle cx="500" cy="500" r="350" stroke="#5a79c8" stroke-width="5" transform="" stroke-opacity="0.2"></circle>
<circle cx="500" cy="500" r="300" stroke="#5a79c8" stroke-width="5" transform=""></circle>
<circle cx="500" cy="500" r="250" stroke="#5a79c8" stroke-width="5" transform="" stroke-opacity="0.2"></circle>
<circle cx="500" cy="500" r="200" stroke="#5a79c8" stroke-width="5" transform=""></circle>
<circle cx="500" cy="500" r="150" stroke="#5a79c8" stroke-width="5" transform="" stroke-opacity="0.2"></circle>
<circle cx="500" cy="500" r="100" stroke="#5a79c8" fill="#5fb7cf" stroke-width="2" transform="" fill-opacity="0.2"></circle>
<circle cx="500" cy="500" r="50" stroke="#5a79c8" stroke-width="2" transform="" stroke-opacity="0.2"></circle>

<circle cx="500" cy="500" r="20" stroke="#ffdf91" stroke-width="1.5" transform=""></circle>
<text x="525" y="504" fill="#ffdf91"font-size="15">Helios</text>

<circle cx="-0.00" cy="0" r="10" stroke="black" stroke-width="1" fill="#61a7ff" transform="translate(500,500)"></circle>
<text x="-95.00" y="25" transform="translate(500,480)" fill="#61a7ff" font-size="20">Alioth</text>

<circle cx="5.41" cy="244.09" r="10" stroke="black" stroke-width="1" fill="#e1e1e1" transform="translate(500,500)"></circle>
<text x="-30" y="270" transform="translate(480,480)" fill="#e1e1e1" font-size="20">Ion</text>

<circle cx="35.41" cy="214.09" r="10" stroke="black" stroke-width="1" fill="#7c90ff" transform="translate(500,500)"></circle>
<text x="-62" y="239" transform="translate(500,480)" fill="#7c90ff" font-size="20">Symeon</text>

<circle cx="202.16" cy="-136.66" r="10" stroke="black" stroke-width="1" fill="#bddeff" transform="translate(500,500)"></circle>
<text x="112" y="-109" transform="translate(500,480)" fill="#bddeff" font-size="20">Teoma</text>

<circle cx="-33.09" cy="-139.41" r="10" stroke="black" stroke-width="1" fill="#cfffdd" transform="translate(500,500)"></circle>
<text x="-139" y="-112" transform="translate(500,480)" fill="#cfffdd" font-size="20">Talemai</text>

<circle cx="-108.84" cy="-56.41" r="10" stroke="black" stroke-width="1" fill="#c6a668" transform="translate(500,500)"></circle>
<text x="-164" y="-30" transform="translate(500,480)" fill="#c6a668" font-size="20">Feli</text>

<circle cx="-235.34" cy="-31.91" r="10" stroke="black" stroke-width="1" fill="#8dc0e5" transform="translate(500,500)"></circle>
<text x="-297" y="-5" transform="translate(500,480)" fill="#8dc0e5" font-size="20">Jago</text>

<circle cx="43.66" cy="-56.66" r="10" stroke="black" stroke-width="1" fill="#8a9fff" transform="translate(500,500)"></circle>
<text x="-38" y="-28" transform="translate(500,480)" fill="#8a9fff" font-size="20">Madis</text>

<circle cx="72.91" cy="-27.16" r="10" stroke="black" stroke-width="1" fill="#ffaf6a" transform="translate(500,500)"></circle>
<text x="88" y="-7" transform="translate(500,485)" fill="#ffaf6a" font-size="20">Thades</text>

<circle cx="247.16" cy="33.84" r="10" stroke="black" stroke-width="1" fill="#cacaca" transform="translate(500,500)"></circle>
<text x="263" y="62" transform="translate(500,480)" fill="#cacaca" font-size="20">Lacobus</text>

<circle cx="-108.84" cy="-56.41" r="10" stroke="black" stroke-width="1" fill="#c6a668" transform="translate(500,500)"></circle>
<text x="-165.84" y="-30.41" transform="translate(500,480)" fill="#c6a668" font-size="20">Feli</text>

<circle cx="131.91" cy="-67.91" r="10" stroke="black" stroke-width="1" fill="#ffac71" transform="translate(500,500)"></circle>
<text x="81" y="-40" transform="translate(475,480)" fill="#ffac71" font-size="20">Sicari</text>

<circle cx="146.66" cy="-74.16" r="10" stroke="black" stroke-width="1" fill="#ffa775" transform="translate(500,500)"></circle>
<text x="146" y="-46" transform="translate(515,480)" fill="#ffa775" 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="#ffb400" 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">&nbsp;&nbsp;&nbsp;&nbsp;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_12">
    <rect rx="10" id="svg_1" height="50" width="230" y="30" x="15" stroke-width="4" stroke="#aee4ed" fill="#5fb7cf"/>
    <rect rx="10" id="svg_3" height="50" width="230" y="105" x="15" stroke-width="4" stroke="#aee4ed" fill="#5fb7cf"/>
    <rect rx="10" id="svg_7" height="50" width="230" y="180" x="15" stroke-width="4" stroke="#aee4ed" fill="#5fb7cf"/>
    <rect rx="10" id="svg_9" height="50" width="230" y="255" x="15" stroke-width="4" stroke="#aee4ed" fill="#5fb7cf"/>
    <rect rx="10" id="svg_10" height="50" width="230" y="330" x="15" stroke-width="4" stroke="#aee4ed" fill="#5fb7cf"/>
    <rect rx="10" id="svg_11" height="50" width="230" y="405" x="15" stroke-width="4" stroke="#aee4ed" fill="#5fb7cf"/>
   </g>
  <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="30" id="svg_8" y="75" x="40" stroke-width="0" fill="black">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="30" id="svg_14" y="175" x="40" stroke-width="0" fill="black">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="30" id="svg_17" y="275" x="40" stroke-width="0" fill="black">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="30" id="svg_20" y="372" x="40" stroke-width="0" fill="black">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="30" id="svg_23" y="469" x="40" stroke-width="0" fill="black">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="30" id="svg_26" y="568" x="40" stroke-width="0" fill="black">Sicari  :]]..distsicari..[[ SU</text>
  </g>
  <g id="svg_40">
   <g id="svg_39">
    <rect rx="10" id="svg_33" height="50" width="230" y="30" x="780" stroke-width="4" stroke="#aee4ed" fill="#5fb7cf"/>
    <rect rx="10" id="svg_34" height="50" width="230" y="105" x="780" stroke-width="4" stroke="#aee4ed" fill="#5fb7cf"/>
    <rect rx="10" id="svg_35" height="50" width="230" y="180" x="780" stroke-width="4" stroke="#aee4ed" fill="#5fb7cf"/>
    <rect rx="10" id="svg_36" height="50" width="230" y="255" x="780" stroke-width="4" stroke="#aee4ed" fill="#5fb7cf"/>
    <rect rx="10" id="svg_37" height="50" width="230" y="330" x="780" stroke-width="4" stroke="#aee4ed" fill="#5fb7cf"/>
    <rect rx="10" id="svg_38" height="50" width="230" y="405" x="780" stroke-width="4" stroke="#aee4ed" fill="#5fb7cf"/>
    </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_25" y="75" x="1007.163642" stroke-width="0" fill="black">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="30" id="svg_27" y="175" x="1007.163642" stroke-width="0" fill="black">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="30" id="svg_28" y="275" x="1007.163642" stroke-width="0" fill="black">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="30" id="svg_30" y="372" x="1007.163642" stroke-width="0" fill="black">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="30" id="svg_31" y="469" x="1007.163642" stroke-width="0" fill="black">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="30" id="svg_32" y="568" x="1007.163642" stroke-width="0" fill="black">Lacobus  :]]..distlacobus..[[ SU</text>

  </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="#976eb0">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="#976eb0">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="#976eb0">TTD: ]]..time_to_distance..[[ </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="#976eb0">VELOC: ]]..shipVelocity..[[ km/h</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="900" stroke-width="0" fill="#976eb0">DEST: ]]..display_selection..[[ </text>

</svg>

    ]])

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


 

Could you explain how to do this update like i'm a caveman? xD

Link to comment
Share on other sites

  • 4 weeks later...

 

First, the idea and functionality is great,... BUT

 

What is for i=1,1 do  .... for i=2,2 do... ???

It's a no-loop/loop

such unusual code often leads to errors, whatever the purpose?

 

here my lines and works identical to the no-loop/loops

 

table.insert(MapScreenButtons, {id = ("b1"), enabled=true, td="<td>", top=2/100, bottom=13/100, left=1/100, right=28/100})
table.insert(MapScreenButtons, {id = ("b2"), enabled=true, td="<td>", top=15/100, bottom=26/100, left=1/100, right=30/100})
table.insert(MapScreenButtons, {id = ("b3"), enabled=true, td="<td>", top=27/100, bottom=38/100, left=1/100, right=28/100})
table.insert(MapScreenButtons, {id = ("b4"), enabled=true, td="<td>", top=39/100, bottom=50/100, left=1/100, right=28/100})
table.insert(MapScreenButtons, {id = ("b5"), enabled=true, td="<td>", top=51/100, bottom=62/100, left=1/100, right=28/100})
table.insert(MapScreenButtons, {id = ("b6"), enabled=true, td="<td>", top=64/100, bottom=75/100, left=1/100, right=28/100})
table.insert(MapScreenButtons, {id = ("b7"), enabled=true, td="<td>", top=2/100, bottom=13/100, left=75/100, right=100/100})
table.insert(MapScreenButtons, {id = ("b8"), enabled=true, td="<td>", top=15/100, bottom=26/100, left=75/100, right=100/100})
table.insert(MapScreenButtons, {id = ("b9"), enabled=true, td="<td>", top=27/100, bottom=38/100, left=75/100, right=100/100})
table.insert(MapScreenButtons, {id = ("b10"), enabled=true, td="<td>", top=39/100, bottom=50/100, left=75/100, right=100/100})
table.insert(MapScreenButtons, {id = ("b11"), enabled=true, td="<td>", top=51/100, bottom=62/100, left=75/100, right=100/100})
table.insert(MapScreenButtons, {id = ("b12"), enabled=true, td="<td>", top=64/100, bottom=75/100, left=75/100, right=100/100})
table.insert(MapScreenButtons, {id = ("b13"), enabled=true, td="<td>", top=90/100, bottom=100/100, left=1/100, right=18/100})

 

thanks for your greate work

Link to comment
Share on other sites

  • 4 weeks later...
  • 6 months later...

I have followed the installation instructions. The map displays, but I am unable to click on any of the buttons. Anyone have a clue how to fix this please?

 

Thanks.

 

EDIT: Nevermind, fixed it.

Edited by AcidTDP
Link to comment
Share on other sites

  • 3 months later...
  • 11 months later...

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...