Move all the logic from eos graph to gui graph for warp time
Now backend graphs have to be aware of handles used in UI graphs, so why not
This commit is contained in:
@@ -27,26 +27,7 @@ class Graph(metaclass=ABCMeta):
|
||||
def __init__(self):
|
||||
self._cache = {}
|
||||
|
||||
@abstractmethod
|
||||
def getPlotPoints(self, mainInput, miscInputs, xSpec, ySpec, fit, tgt):
|
||||
raise NotImplementedError
|
||||
|
||||
_normalizers = {}
|
||||
|
||||
def _normalizeParams(self, mainInput, miscInputs, fit, tgt):
|
||||
if mainInput.unit in self._normalizers:
|
||||
normalizer = self._normalizers[mainInput.unit]
|
||||
newMainInput = [mainInput.handle, tuple(normalizer(v) for v in mainInput.value)]
|
||||
else:
|
||||
newMainInput = [mainInput.handle, mainInput.value]
|
||||
newMiscInputs = []
|
||||
for miscInput in miscInputs:
|
||||
if miscInput.unit in self._normalizers:
|
||||
newMiscInput = [miscInput.handle, self._normalizers[miscInput.unit](miscInput.value)]
|
||||
else:
|
||||
newMiscInput = [miscInput.handle, miscInput.value]
|
||||
newMiscInputs.append(newMiscInput)
|
||||
return newMainInput, newMiscInputs
|
||||
|
||||
### Old stuff
|
||||
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
import math
|
||||
|
||||
from eos.const import FittingModuleState
|
||||
from .base import Graph
|
||||
|
||||
|
||||
AU_METERS = 149597870700
|
||||
|
||||
|
||||
class FitWarpTimeGraph(Graph):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.subwarpSpeed = None
|
||||
|
||||
def getPlotPoints(self, mainInput, miscInputs, xSpec, ySpec, fit, tgt):
|
||||
mainInput, miscInputs = self._normalizeParams(mainInput, miscInputs, fit, tgt)
|
||||
|
||||
# limit all parameters to be within limits (time)
|
||||
# pick getter according to x handle and y handle and run it
|
||||
# un-render returned parameters if passed x is relative
|
||||
return [], []
|
||||
|
||||
_normalizers = {
|
||||
'AU': lambda x: x * AU_METERS,
|
||||
'km': lambda x: x * 1000}
|
||||
|
||||
|
||||
def getYForX(self, fit, extraData, distance):
|
||||
if distance == 0:
|
||||
return 0
|
||||
if fit.ID not in self._cache:
|
||||
self.__generateCache(fit)
|
||||
maxWarpSpeed = fit.warpSpeed
|
||||
subwarpSpeed = self._cache[fit.ID]['cleanSubwarpSpeed']
|
||||
time = calculate_time_in_warp(maxWarpSpeed, subwarpSpeed, distance * AU_METERS)
|
||||
return time
|
||||
|
||||
def _getXLimits(self, fit, extraData):
|
||||
return 0, fit.maxWarpDistance
|
||||
|
||||
def __generateCache(self, fit):
|
||||
modStates = {}
|
||||
for mod in fit.modules:
|
||||
if mod.item is not None and mod.item.group.name in ('Propulsion Module', 'Mass Entanglers', 'Cloaking Device') and mod.state >= FittingModuleState.ACTIVE:
|
||||
modStates[mod] = mod.state
|
||||
mod.state = FittingModuleState.ONLINE
|
||||
projFitStates = {}
|
||||
for projFit in fit.projectedFits:
|
||||
projectionInfo = projFit.getProjectionInfo(fit.ID)
|
||||
if projectionInfo is not None and projectionInfo.active:
|
||||
projFitStates[projectionInfo] = projectionInfo.active
|
||||
projectionInfo.active = False
|
||||
projModStates = {}
|
||||
for mod in fit.projectedModules:
|
||||
if not mod.isExclusiveSystemEffect and mod.state >= FittingModuleState.ACTIVE:
|
||||
projModStates[mod] = mod.state
|
||||
mod.state = FittingModuleState.ONLINE
|
||||
projDroneStates = {}
|
||||
for drone in fit.projectedDrones:
|
||||
if drone.amountActive > 0:
|
||||
projDroneStates[drone] = drone.amountActive
|
||||
drone.amountActive = 0
|
||||
projFighterStates = {}
|
||||
for fighter in fit.projectedFighters:
|
||||
if fighter.active:
|
||||
projFighterStates[fighter] = fighter.active
|
||||
fighter.active = False
|
||||
fit.calculateModifiedAttributes()
|
||||
self._cache[fit.ID] = {'cleanSubwarpSpeed': fit.ship.getModifiedItemAttr('maxVelocity')}
|
||||
for projInfo, state in projFitStates.items():
|
||||
projInfo.active = state
|
||||
for mod, state in modStates.items():
|
||||
mod.state = state
|
||||
for mod, state in projModStates.items():
|
||||
mod.state = state
|
||||
for drone, amountActive in projDroneStates.items():
|
||||
drone.amountActive = amountActive
|
||||
for fighter, state in projFighterStates.items():
|
||||
fighter.active = state
|
||||
fit.calculateModifiedAttributes()
|
||||
|
||||
|
||||
# Taken from https://wiki.eveuniversity.org/Warp_time_calculation#Implementation
|
||||
# with minor modifications
|
||||
# Warp speed in AU/s, subwarp speed in m/s, distance in m
|
||||
def calculate_time_in_warp(max_warp_speed, max_subwarp_speed, warp_dist):
|
||||
|
||||
k_accel = max_warp_speed
|
||||
k_decel = min(max_warp_speed / 3, 2)
|
||||
|
||||
warp_dropout_speed = max_subwarp_speed / 2
|
||||
max_ms_warp_speed = max_warp_speed * AU_METERS
|
||||
|
||||
accel_dist = AU_METERS
|
||||
decel_dist = max_ms_warp_speed / k_decel
|
||||
|
||||
minimum_dist = accel_dist + decel_dist
|
||||
|
||||
cruise_time = 0
|
||||
|
||||
if minimum_dist > warp_dist:
|
||||
max_ms_warp_speed = warp_dist * k_accel * k_decel / (k_accel + k_decel)
|
||||
else:
|
||||
cruise_time = (warp_dist - minimum_dist) / max_ms_warp_speed
|
||||
|
||||
accel_time = math.log(max_ms_warp_speed / k_accel) / k_accel
|
||||
decel_time = math.log(max_ms_warp_speed / warp_dropout_speed) / k_decel
|
||||
|
||||
total_time = cruise_time + accel_time + decel_time
|
||||
return total_time
|
||||
@@ -1,6 +1,6 @@
|
||||
# noinspection PyUnresolvedReferences
|
||||
from gui.builtinGraphs import ( # noqa: E402,F401
|
||||
fitDamageStats,
|
||||
# fitDamageStats,
|
||||
# fitDmgVsTime,
|
||||
# fitShieldRegenVsShieldPerc,
|
||||
# fitShieldAmountVsTime,
|
||||
|
||||
@@ -22,18 +22,24 @@ from abc import ABCMeta, abstractmethod
|
||||
from collections import OrderedDict, namedtuple
|
||||
|
||||
|
||||
YDef = namedtuple('YDef', ('handle', 'unit', 'label'))
|
||||
XDef = namedtuple('XDef', ('handle', 'unit', 'label', 'mainInput'))
|
||||
Input = namedtuple('Input', ('handle', 'unit', 'label', 'iconID', 'defaultValue', 'defaultRange', 'mainOnly'))
|
||||
VectorDef = namedtuple('VectorDef', ('lengthHandle', 'lengthUnit', 'angleHandle', 'angleUnit', 'label'))
|
||||
|
||||
|
||||
class Graph(metaclass=ABCMeta):
|
||||
|
||||
# UI stuff
|
||||
views = []
|
||||
yTypes = None
|
||||
|
||||
@classmethod
|
||||
def register(cls):
|
||||
Graph.views.append(cls)
|
||||
|
||||
def __init__(self, eosGraph):
|
||||
self._eosGraph = eosGraph
|
||||
self._cache = {}
|
||||
def __init__(self):
|
||||
self._plotCache = {}
|
||||
self._calcCache = {}
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
@@ -84,25 +90,112 @@ class Graph(metaclass=ABCMeta):
|
||||
|
||||
def getPlotPoints(self, mainInput, miscInputs, xSpec, ySpec, fit, tgt=None):
|
||||
try:
|
||||
plotData = self._cache[fit.ID][(ySpec, xSpec)]
|
||||
plotData = self._plotCache[fit.ID][(ySpec, xSpec)]
|
||||
except KeyError:
|
||||
plotData = self._eosGraph.getPlotPoints(mainInput, miscInputs, xSpec, ySpec, fit, tgt)
|
||||
fitCache = self._cache.setdefault(fit.ID, {})
|
||||
plotData = self._calcPlotPoints(mainInput, miscInputs, xSpec, ySpec, fit, tgt)
|
||||
fitCache = self._plotCache.setdefault(fit.ID, {})
|
||||
fitCache[(ySpec, xSpec)] = plotData
|
||||
return plotData
|
||||
|
||||
def clearCache(self, key=None):
|
||||
if key is None:
|
||||
self._cache.clear()
|
||||
elif key in self._cache:
|
||||
del self._cache[key]
|
||||
self._eosGraph.clearCache(key=key)
|
||||
self._plotCache.clear()
|
||||
self._calcCache.clear()
|
||||
if key in self._plotCache:
|
||||
del self._plotCache[key]
|
||||
if key in self._calcCache:
|
||||
del self._calcCache[key]
|
||||
|
||||
# Calculation stuff
|
||||
def _calcPlotPoints(self, mainInput, miscInputs, xSpec, ySpec, fit, tgt):
|
||||
mainInput, miscInputs = self._normalizeParams(mainInput, miscInputs, fit, tgt)
|
||||
mainInput, miscInputs = self._limitParams(mainInput, miscInputs, fit, tgt)
|
||||
xs, ys = self._getPoints(mainInput, miscInputs, xSpec, ySpec, fit, tgt)
|
||||
xs = self._denormalizeValues(xs, xSpec, fit, tgt)
|
||||
ys = self._denormalizeValues(ys, ySpec, fit, tgt)
|
||||
return xs, ys
|
||||
|
||||
YDef = namedtuple('YDef', ('handle', 'unit', 'label'))
|
||||
XDef = namedtuple('XDef', ('handle', 'unit', 'label', 'mainInput'))
|
||||
Input = namedtuple('Input', ('handle', 'unit', 'label', 'iconID', 'defaultValue', 'defaultRange', 'mainOnly'))
|
||||
VectorDef = namedtuple('VectorDef', ('lengthHandle', 'lengthUnit', 'angleHandle', 'angleUnit', 'label'))
|
||||
_normalizers = {}
|
||||
|
||||
def _normalizeParams(self, mainInput, miscInputs, fit, tgt):
|
||||
key = (mainInput.handle, mainInput.unit)
|
||||
if key in self._normalizers:
|
||||
normalizer = self._normalizers[key]
|
||||
newMainInput = (mainInput.handle, tuple(normalizer(v, fit, tgt) for v in mainInput.value))
|
||||
else:
|
||||
newMainInput = (mainInput.handle, mainInput.value)
|
||||
newMiscInputs = []
|
||||
for miscInput in miscInputs:
|
||||
key = (miscInput.handle, miscInput.unit)
|
||||
if key in self._normalizers:
|
||||
normalizer = self._normalizers[key]
|
||||
newMiscInput = (miscInput.handle, normalizer(miscInput.value))
|
||||
else:
|
||||
newMiscInput = (miscInput.handle, miscInput.value)
|
||||
newMiscInputs.append(newMiscInput)
|
||||
return newMainInput, newMiscInputs
|
||||
|
||||
_limiters = {}
|
||||
|
||||
def _limitParams(self, mainInput, miscInputs, fit, tgt):
|
||||
|
||||
def limitToRange(val, limitRange):
|
||||
if val is None:
|
||||
return None
|
||||
val = max(val, min(limitRange))
|
||||
val = min(val, max(limitRange))
|
||||
return val
|
||||
|
||||
mainHandle, mainValue = mainInput
|
||||
if mainHandle in self._limiters:
|
||||
limiter = self._limiters[mainHandle]
|
||||
newMainInput = (mainHandle, tuple(limitToRange(v, limiter(fit, tgt)) for v in mainValue))
|
||||
else:
|
||||
newMainInput = mainInput
|
||||
newMiscInputs = []
|
||||
for miscInput in miscInputs:
|
||||
miscHandle, miscValue = miscInput
|
||||
if miscHandle in self._limiters:
|
||||
limiter = self._limiters[miscHandle]
|
||||
newMiscInput = (miscHandle, limitToRange(miscValue, limiter(fit, tgt)))
|
||||
newMiscInputs.append(newMiscInput)
|
||||
else:
|
||||
newMiscInputs.append(miscInput)
|
||||
return newMainInput, newMiscInputs
|
||||
|
||||
_getters = {}
|
||||
|
||||
def _getPoints(self, mainInput, miscInputs, xSpec, ySpec, fit, tgt):
|
||||
try:
|
||||
getter = self._getters[(xSpec.handle, ySpec.handle)]
|
||||
except KeyError:
|
||||
return [], []
|
||||
else:
|
||||
return getter(self, mainInput, miscInputs, fit, tgt)
|
||||
|
||||
_denormalizers = {}
|
||||
|
||||
def _denormalizeValues(self, values, axisSpec, fit, tgt):
|
||||
key = (axisSpec.handle, axisSpec.unit)
|
||||
if key in self._denormalizers:
|
||||
denormalizer = self._denormalizers[key]
|
||||
values = [denormalizer(v, fit, tgt) for v in values]
|
||||
return values
|
||||
|
||||
def _iterLinear(self, valRange, resolution=100):
|
||||
rangeLow = min(valRange)
|
||||
rangeHigh = max(valRange)
|
||||
# Amount is amount of ranges between points here, not amount of points
|
||||
step = (rangeHigh - rangeLow) / resolution
|
||||
if step == 0:
|
||||
yield rangeLow
|
||||
else:
|
||||
current = rangeLow
|
||||
# Take extra half step to make sure end of range is always included
|
||||
# despite any possible float errors
|
||||
while current <= (rangeHigh + step / 2):
|
||||
yield current
|
||||
current += step
|
||||
|
||||
|
||||
# noinspection PyUnresolvedReferences
|
||||
|
||||
@@ -18,17 +18,23 @@
|
||||
# =============================================================================
|
||||
|
||||
|
||||
from eos.graph.fitWarpTime import FitWarpTimeGraph as EosGraph
|
||||
import math
|
||||
|
||||
from eos.const import FittingModuleState
|
||||
from .base import Graph, XDef, YDef, Input
|
||||
|
||||
|
||||
AU_METERS = 149597870700
|
||||
|
||||
|
||||
class FitWarpTimeGraph(Graph):
|
||||
|
||||
name = 'Warp Time'
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(EosGraph())
|
||||
super().__init__()
|
||||
|
||||
# UI stuff
|
||||
@property
|
||||
def xDefs(self):
|
||||
return [
|
||||
@@ -45,5 +51,110 @@ class FitWarpTimeGraph(Graph):
|
||||
Input(handle='distance', unit='AU', label='Distance', iconID=1391, defaultValue=50, defaultRange=(0, 50), mainOnly=False),
|
||||
Input(handle='distance', unit='km', label='Distance', iconID=1391, defaultValue=10000, defaultRange=(150, 5000), mainOnly=False)]
|
||||
|
||||
# Calculation stuff
|
||||
_normalizers = {
|
||||
('distance', 'AU'): lambda v, fit, tgt: v * AU_METERS,
|
||||
('distance', 'km'): lambda v, fit, tgt: v * 1000}
|
||||
|
||||
_limiters = {
|
||||
'distance': lambda fit, tgt: (0, fit.maxWarpDistance * AU_METERS)}
|
||||
|
||||
_denormalizers = {
|
||||
('distance', 'AU'): lambda v, fit, tgt: v / AU_METERS,
|
||||
('distance', 'km'): lambda v, fit, tgt: v / 1000}
|
||||
|
||||
def _distance2time(self, mainInput, miscInputs, fit, tgt):
|
||||
xs = []
|
||||
ys = []
|
||||
subwarpSpeed = self.__getSubwarpSpeed(fit)
|
||||
warpSpeed = fit.warpSpeed
|
||||
for distance in self._iterLinear(mainInput[1]):
|
||||
time = calculate_time_in_warp(subwarpSpeed, warpSpeed, distance)
|
||||
xs.append(distance)
|
||||
ys.append(time)
|
||||
return xs, ys
|
||||
|
||||
_getters = {
|
||||
('distance', 'time'): _distance2time}
|
||||
|
||||
def __getSubwarpSpeed(self, fit):
|
||||
try:
|
||||
subwarpSpeed = self._calcCache[fit.ID]['cleanSubwarpSpeed']
|
||||
except KeyError:
|
||||
modStates = {}
|
||||
for mod in fit.modules:
|
||||
if mod.item is not None and mod.item.group.name in ('Propulsion Module', 'Mass Entanglers', 'Cloaking Device') and mod.state >= FittingModuleState.ACTIVE:
|
||||
modStates[mod] = mod.state
|
||||
mod.state = FittingModuleState.ONLINE
|
||||
projFitStates = {}
|
||||
for projFit in fit.projectedFits:
|
||||
projectionInfo = projFit.getProjectionInfo(fit.ID)
|
||||
if projectionInfo is not None and projectionInfo.active:
|
||||
projFitStates[projectionInfo] = projectionInfo.active
|
||||
projectionInfo.active = False
|
||||
projModStates = {}
|
||||
for mod in fit.projectedModules:
|
||||
if not mod.isExclusiveSystemEffect and mod.state >= FittingModuleState.ACTIVE:
|
||||
projModStates[mod] = mod.state
|
||||
mod.state = FittingModuleState.ONLINE
|
||||
projDroneStates = {}
|
||||
for drone in fit.projectedDrones:
|
||||
if drone.amountActive > 0:
|
||||
projDroneStates[drone] = drone.amountActive
|
||||
drone.amountActive = 0
|
||||
projFighterStates = {}
|
||||
for fighter in fit.projectedFighters:
|
||||
if fighter.active:
|
||||
projFighterStates[fighter] = fighter.active
|
||||
fighter.active = False
|
||||
fit.calculateModifiedAttributes()
|
||||
subwarpSpeed = fit.ship.getModifiedItemAttr('maxVelocity')
|
||||
self._calcCache[fit.ID] = {'cleanSubwarpSpeed': subwarpSpeed}
|
||||
for projInfo, state in projFitStates.items():
|
||||
projInfo.active = state
|
||||
for mod, state in modStates.items():
|
||||
mod.state = state
|
||||
for mod, state in projModStates.items():
|
||||
mod.state = state
|
||||
for drone, amountActive in projDroneStates.items():
|
||||
drone.amountActive = amountActive
|
||||
for fighter, state in projFighterStates.items():
|
||||
fighter.active = state
|
||||
fit.calculateModifiedAttributes()
|
||||
return subwarpSpeed
|
||||
|
||||
|
||||
# Taken from https://wiki.eveuniversity.org/Warp_time_calculation#Implementation
|
||||
# with minor modifications
|
||||
# Warp speed in AU/s, subwarp speed in m/s, distance in m
|
||||
def calculate_time_in_warp(max_warp_speed, max_subwarp_speed, warp_dist):
|
||||
|
||||
if warp_dist == 0:
|
||||
return 0
|
||||
|
||||
k_accel = max_warp_speed
|
||||
k_decel = min(max_warp_speed / 3, 2)
|
||||
|
||||
warp_dropout_speed = max_subwarp_speed / 2
|
||||
max_ms_warp_speed = max_warp_speed * AU_METERS
|
||||
|
||||
accel_dist = AU_METERS
|
||||
decel_dist = max_ms_warp_speed / k_decel
|
||||
|
||||
minimum_dist = accel_dist + decel_dist
|
||||
|
||||
cruise_time = 0
|
||||
|
||||
if minimum_dist > warp_dist:
|
||||
max_ms_warp_speed = warp_dist * k_accel * k_decel / (k_accel + k_decel)
|
||||
else:
|
||||
cruise_time = (warp_dist - minimum_dist) / max_ms_warp_speed
|
||||
|
||||
accel_time = math.log(max_ms_warp_speed / k_accel) / k_accel
|
||||
decel_time = math.log(max_ms_warp_speed / warp_dropout_speed) / k_decel
|
||||
|
||||
total_time = cruise_time + accel_time + decel_time
|
||||
return total_time
|
||||
|
||||
|
||||
FitWarpTimeGraph.register()
|
||||
|
||||
Reference in New Issue
Block a user