Rework warp time graph to use new getter approach

This commit is contained in:
DarkPhoenix
2019-08-02 09:36:22 +03:00
parent 4ddbdebae4
commit 62fbb7c9c8
7 changed files with 278 additions and 199 deletions

View File

@@ -1,9 +1,7 @@
# noinspection PyUnresolvedReferences
from gui.builtinGraphs import ( # noqa: E402,F401
fitDamageStats,
# fitDmgVsTime,
fitShieldRegen,
fitCapRegen,
fitMobility,
fitWarpTime
)
fitWarpTime)

View File

@@ -232,13 +232,12 @@ class FitGraph(metaclass=ABCMeta):
def _getPoints(self, mainParam, miscParams, xSpec, ySpec, fit, tgt):
try:
fullGetter, singleGetter, cacheGetter = self._getters[(xSpec.handle, ySpec.handle)]
getterClass = self._getters[(xSpec.handle, ySpec.handle)]
except KeyError:
return [], []
else:
return fullGetter(
self, cacheGetter=cacheGetter, singleGetter=singleGetter,
mainParam=mainParam, miscParams=miscParams, fit=fit, tgt=tgt)
getter = getterClass(graph=self)
return getter.getRange(mainParam=mainParam, miscParams=miscParams, fit=fit, tgt=tgt)
_denormalizers = {}
@@ -249,11 +248,52 @@ class FitGraph(metaclass=ABCMeta):
values = [denormalizer(v, fit, tgt) for v in values]
return values
def _iterLinear(self, valRange, segments=200):
class PointGetter(metaclass=ABCMeta):
def __init__(self, graph):
self.graph = graph
@abstractmethod
def getRange(self, mainParam, miscParams, fit, tgt):
raise NotImplementedError
@abstractmethod
def getPoint(self, mainParam, miscParams, fit, tgt, cache=None):
raise NotImplementedError
class SmoothPointGetter(PointGetter, metaclass=ABCMeta):
def __init__(self, graph, baseResolution=200):
super().__init__(graph)
self._baseResolution = baseResolution
def getRange(self, mainParam, miscParams, fit, tgt):
xs = []
ys = []
commonData = self._getCommonData(miscParams=miscParams, fit=fit, tgt=tgt)
for x in self._iterLinear(mainParam[1]):
y = self._calculatePoint(
mainParam=x, miscParams=miscParams,
fit=fit, tgt=tgt, commonData=commonData)
xs.append(x)
ys.append(y)
return xs, ys
def getPoint(self, mainParam, miscParams, fit, tgt):
commonData = self._getCommonData(miscParams=miscParams, fit=fit, tgt=tgt)
y = self._calculatePoint(
mainParam=mainParam, miscParams=miscParams,
fit=fit, tgt=tgt, commonData=commonData)
return mainParam, y
def _iterLinear(self, valRange):
rangeLow = min(valRange)
rangeHigh = max(valRange)
# Amount is amount of ranges between points here, not amount of points
step = (rangeHigh - rangeLow) / segments
# Resolution defines amount of ranges between points here,
# not amount of points
step = (rangeHigh - rangeLow) / self._baseResolution
if step == 0 or math.isnan(step):
yield rangeLow
else:
@@ -264,6 +304,13 @@ class FitGraph(metaclass=ABCMeta):
yield current
current += step
def _getCommonData(self, miscParams, fit, tgt):
return {}
@abstractmethod
def _calculatePoint(self, mainParam, miscParams, fit, tgt, commonData):
raise NotImplementedError
class FitDataCache:

View File

@@ -1,189 +0,0 @@
# =============================================================================
# Copyright (C) 2010 Diego Duclos
#
# This file is part of pyfa.
#
# pyfa is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# pyfa is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
# =============================================================================
import math
from eos.const import FittingModuleState
from service.const import GraphCacheCleanupReason
from .base import FitGraph, XDef, YDef, Input, FitDataCache
AU_METERS = 149597870700
class FitWarpTimeGraph(FitGraph):
def __init__(self):
super().__init__()
self._subspeedCache = SubwarpSpeedCache()
def _clearInternalCache(self, reason, extraData):
if reason in (GraphCacheCleanupReason.fitChanged, GraphCacheCleanupReason.fitRemoved):
self._subspeedCache.clearForFit(extraData)
elif reason == GraphCacheCleanupReason.graphSwitched:
self._subspeedCache.clearAll()
# UI stuff
internalName = 'warpTimeGraph'
name = 'Warp Time'
xDefs = [
XDef(handle='distance', unit='AU', label='Distance', mainInput=('distance', 'AU')),
XDef(handle='distance', unit='km', label='Distance', mainInput=('distance', 'km'))]
yDefs = [
YDef(handle='time', unit='s', label='Warp time')]
inputs = [
Input(handle='distance', unit='AU', label='Distance', iconID=1391, defaultValue=20, defaultRange=(0, 50)),
Input(handle='distance', unit='km', label='Distance', iconID=1391, defaultValue=1000, defaultRange=(150, 5000))]
srcExtraCols = ('WarpSpeed', 'WarpDistance')
# 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 _distance2timeCache(self, miscParams, fit, tgt):
return {
'subwarpSpeed': self._subspeedCache.getSubwarpSpeed(fit),
'warpSpeed': fit.warpSpeed}
def _distance2timeFull(self, cacheGetter, singleGetter, mainParam, miscParams, fit, tgt):
xs = []
ys = []
cache = cacheGetter(self, miscParams=miscParams, fit=fit, tgt=tgt)
for distance in self._iterLinear(mainParam[1]):
time = singleGetter(
self, cacheGetter=cacheGetter, mainParam=distance, miscParams=miscParams,
fit=fit, tgt=tgt, cache=cache)
xs.append(distance)
ys.append(time)
return xs, ys
def _distance2timeSingle(self, cacheGetter, mainParam, miscParams, fit, tgt, cache=None):
if cache is None:
cache = cacheGetter(self, miscParams=miscParams, fit=fit, tgt=tgt)
time = calculate_time_in_warp(
max_subwarp_speed=cache['subwarpSpeed'],
max_warp_speed=cache['warpSpeed'],
warp_dist=mainParam)
return time
_getters = {
('distance', 'time'): (_distance2timeFull, _distance2timeSingle, _distance2timeCache)}
class SubwarpSpeedCache(FitDataCache):
def getSubwarpSpeed(self, fit):
try:
subwarpSpeed = self._data[fit.ID]
except KeyError:
modStates = {}
disallowedGroups = (
# Active modules which affect ship speed and cannot be used in warp
'Propulsion Module',
'Mass Entanglers',
'Cloaking Device',
# Those reduce ship speed to 0
'Siege Module',
'Super Weapon',
'Cynosural Field Generator',
'Clone Vat Bay',
'Jump Portal Generator')
for mod in fit.modules:
if mod.item is not None and mod.item.group.name in disallowedGroups 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._data[fit.ID] = 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()

View File

@@ -0,0 +1 @@
import gui.builtinGraphs.fitWarpTime.graph # noqa: E402,F401

View File

@@ -0,0 +1,74 @@
# =============================================================================
# Copyright (C) 2010 Diego Duclos
#
# This file is part of pyfa.
#
# pyfa is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# pyfa is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
# =============================================================================
import math
from gui.builtinGraphs.base import SmoothPointGetter
AU_METERS = 149597870700
class Distance2TimeGetter(SmoothPointGetter):
def _getCommonData(self, miscParams, fit, tgt):
return {
'subwarpSpeed': self.graph._subspeedCache.getSubwarpSpeed(fit),
'warpSpeed': fit.warpSpeed}
def _calculatePoint(self, mainParam, miscParams, fit, tgt, commonData):
time = calculate_time_in_warp(
max_subwarp_speed=commonData['subwarpSpeed'],
max_warp_speed=commonData['warpSpeed'],
warp_dist=mainParam)
return time
# 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

View File

@@ -0,0 +1,66 @@
# =============================================================================
# Copyright (C) 2010 Diego Duclos
#
# This file is part of pyfa.
#
# pyfa is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# pyfa is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
# =============================================================================
from service.const import GraphCacheCleanupReason
from gui.builtinGraphs.base import FitGraph, XDef, YDef, Input
from .getter import Distance2TimeGetter, AU_METERS
from .subwarpCache import SubwarpSpeedCache
class FitWarpTimeGraph(FitGraph):
def __init__(self):
super().__init__()
self._subspeedCache = SubwarpSpeedCache()
def _clearInternalCache(self, reason, extraData):
if reason in (GraphCacheCleanupReason.fitChanged, GraphCacheCleanupReason.fitRemoved):
self._subspeedCache.clearForFit(extraData)
elif reason == GraphCacheCleanupReason.graphSwitched:
self._subspeedCache.clearAll()
# UI stuff
internalName = 'warpTimeGraph'
name = 'Warp Time'
xDefs = [
XDef(handle='distance', unit='AU', label='Distance', mainInput=('distance', 'AU')),
XDef(handle='distance', unit='km', label='Distance', mainInput=('distance', 'km'))]
yDefs = [
YDef(handle='time', unit='s', label='Warp time')]
inputs = [
Input(handle='distance', unit='AU', label='Distance', iconID=1391, defaultValue=20, defaultRange=(0, 50)),
Input(handle='distance', unit='km', label='Distance', iconID=1391, defaultValue=1000, defaultRange=(150, 5000))]
srcExtraCols = ('WarpSpeed', 'WarpDistance')
# 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}
_getters = {
('distance', 'time'): Distance2TimeGetter}
FitWarpTimeGraph.register()

View File

@@ -0,0 +1,82 @@
# =============================================================================
# Copyright (C) 2010 Diego Duclos
#
# This file is part of pyfa.
#
# pyfa is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# pyfa is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
# =============================================================================
from eos.const import FittingModuleState
from gui.builtinGraphs.base import FitDataCache
class SubwarpSpeedCache(FitDataCache):
def getSubwarpSpeed(self, fit):
try:
subwarpSpeed = self._data[fit.ID]
except KeyError:
modStates = {}
disallowedGroups = (
# Active modules which affect ship speed and cannot be used in warp
'Propulsion Module',
'Mass Entanglers',
'Cloaking Device',
# Those reduce ship speed to 0
'Siege Module',
'Super Weapon',
'Cynosural Field Generator',
'Clone Vat Bay',
'Jump Portal Generator')
for mod in fit.modules:
if mod.item is not None and mod.item.group.name in disallowedGroups 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._data[fit.ID] = 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