From e821b2d09c005835b20904906c70751777cd17e2 Mon Sep 17 00:00:00 2001 From: DarkPhoenix Date: Sat, 3 Aug 2019 23:56:44 +0300 Subject: [PATCH] Store wrappers in graph lists --- graphs/data/base/getter.py | 22 +- graphs/data/base/graph.py | 56 ++-- graphs/data/fitCapRegen/getter.py | 26 +- graphs/data/fitCapRegen/graph.py | 6 +- graphs/data/fitDamageStats/cache/projected.py | 28 +- graphs/data/fitDamageStats/cache/time.py | 64 ++-- .../data/fitDamageStats/calc/application.py | 60 ++-- graphs/data/fitDamageStats/calc/projected.py | 34 +-- graphs/data/fitDamageStats/getter.py | 157 +++++----- graphs/data/fitDamageStats/graph.py | 17 +- graphs/data/fitDamageStats/helper.py | 89 ------ graphs/data/fitMobility/getter.py | 20 +- graphs/data/fitMobility/graph.py | 2 +- graphs/data/fitShieldRegen/getter.py | 26 +- graphs/data/fitShieldRegen/graph.py | 10 +- graphs/data/fitWarpTime/cache.py | 24 +- graphs/data/fitWarpTime/getter.py | 8 +- graphs/data/fitWarpTime/graph.py | 10 +- graphs/gui/frame.py | 32 +- graphs/gui/lists.py | 280 ++++++++---------- graphs/gui/panel.py | 27 +- graphs/wrapper.py | 131 ++++++++ gui/builtinContextMenus/itemRemove.py | 2 +- .../attributeDisplayGraph.py | 7 +- gui/builtinViewColumns/baseIcon.py | 15 +- gui/builtinViewColumns/baseName.py | 18 +- 26 files changed, 604 insertions(+), 567 deletions(-) delete mode 100644 graphs/data/fitDamageStats/helper.py create mode 100644 graphs/wrapper.py diff --git a/graphs/data/base/getter.py b/graphs/data/base/getter.py index 88e7b8381..80537744f 100644 --- a/graphs/data/base/getter.py +++ b/graphs/data/base/getter.py @@ -28,11 +28,11 @@ class PointGetter(metaclass=ABCMeta): self.graph = graph @abstractmethod - def getRange(self, xRange, miscParams, fit, tgt): + def getRange(self, xRange, miscParams, src, tgt): raise NotImplementedError @abstractmethod - def getPoint(self, x, miscParams, fit, tgt): + def getPoint(self, x, miscParams, src, tgt): raise NotImplementedError @@ -41,16 +41,16 @@ class SmoothPointGetter(PointGetter, metaclass=ABCMeta): _baseResolution = 200 _extraDepth = 0 - def getRange(self, xRange, miscParams, fit, tgt): + def getRange(self, xRange, miscParams, src, tgt): xs = [] ys = [] - commonData = self._getCommonData(miscParams=miscParams, fit=fit, tgt=tgt) + commonData = self._getCommonData(miscParams=miscParams, src=src, tgt=tgt) def addExtraPoints(x1, y1, x2, y2, depth): if depth <= 0 or y1 == y2: return newX = (x1 + x2) / 2 - newY = self._calculatePoint(x=newX, miscParams=miscParams, fit=fit, tgt=tgt, commonData=commonData) + newY = self._calculatePoint(x=newX, miscParams=miscParams, src=src, tgt=tgt, commonData=commonData) addExtraPoints(x1=prevX, y1=prevY, x2=newX, y2=newY, depth=depth - 1) xs.append(newX) ys.append(newY) @@ -60,7 +60,7 @@ class SmoothPointGetter(PointGetter, metaclass=ABCMeta): prevY = None # Go through X points defined by our resolution setting for x in self._xIterLinear(xRange): - y = self._calculatePoint(x=x, miscParams=miscParams, fit=fit, tgt=tgt, commonData=commonData) + y = self._calculatePoint(x=x, miscParams=miscParams, src=src, tgt=tgt, commonData=commonData) if prevX is not None and prevY is not None: # And if Y values of adjacent data points are not equal, add extra points # depending on extra depth setting @@ -71,9 +71,9 @@ class SmoothPointGetter(PointGetter, metaclass=ABCMeta): ys.append(y) return xs, ys - def getPoint(self, x, miscParams, fit, tgt): - commonData = self._getCommonData(miscParams=miscParams, fit=fit, tgt=tgt) - return self._calculatePoint(x=x, miscParams=miscParams, fit=fit, tgt=tgt, commonData=commonData) + def getPoint(self, x, miscParams, src, tgt): + commonData = self._getCommonData(miscParams=miscParams, src=src, tgt=tgt) + return self._calculatePoint(x=x, miscParams=miscParams, src=src, tgt=tgt, commonData=commonData) def _xIterLinear(self, xRange): xLow = min(xRange) @@ -87,9 +87,9 @@ class SmoothPointGetter(PointGetter, metaclass=ABCMeta): for i in range(self._baseResolution + 1): yield xLow + step * i - def _getCommonData(self, miscParams, fit, tgt): + def _getCommonData(self, miscParams, src, tgt): return {} @abstractmethod - def _calculatePoint(self, x, miscParams, fit, tgt, commonData): + def _calculatePoint(self, x, miscParams, src, tgt, commonData): raise NotImplementedError diff --git a/graphs/data/base/graph.py b/graphs/data/base/graph.py index 6dd2f9a9a..7e8f52a67 100644 --- a/graphs/data/base/graph.py +++ b/graphs/data/base/graph.py @@ -22,8 +22,6 @@ import math from abc import ABCMeta, abstractmethod from collections import OrderedDict -from eos.saveddata.fit import Fit -from eos.saveddata.targetProfile import TargetProfile from eos.utils.float import floatUnerr from service.const import GraphCacheCleanupReason @@ -91,18 +89,20 @@ class FitGraph(metaclass=ABCMeta): tgtVectorDef = None hasTargets = False - def getPlotPoints(self, mainInput, miscInputs, xSpec, ySpec, fit, tgt=None): - if isinstance(tgt, Fit): + def getPlotPoints(self, mainInput, miscInputs, xSpec, ySpec, src, tgt=None): + if tgt is not None and tgt.isFit: tgtType = 'fit' - elif isinstance(tgt, TargetProfile): + elif tgt is not None and tgt.isProfile: tgtType = 'profile' else: tgtType = None - cacheKey = (fit.ID, tgtType, getattr(tgt, 'ID', None)) + cacheKey = (src.itemID, tgtType, getattr(tgt, 'itemID', None)) try: plotData = self._plotCache[cacheKey][(ySpec, xSpec)] except KeyError: - plotData = self._calcPlotPoints(mainInput, miscInputs, xSpec, ySpec, fit, tgt) + plotData = self._calcPlotPoints( + mainInput=mainInput, miscInputs=miscInputs, + xSpec=xSpec, ySpec=ySpec, src=src, tgt=tgt) self._plotCache.setdefault(cacheKey, {})[(ySpec, xSpec)] = plotData return plotData @@ -136,17 +136,23 @@ class FitGraph(metaclass=ABCMeta): return # Calculation stuff - def _calcPlotPoints(self, mainInput, miscInputs, xSpec, ySpec, fit, tgt): - mainParamRange, miscParams = self._normalizeInputs(mainInput=mainInput, miscInputs=miscInputs, fit=fit, tgt=tgt) - mainParamRange, miscParams = self._limitParams(mainParamRange=mainParamRange, miscParams=miscParams, fit=fit, tgt=tgt) - xs, ys = self._getPoints(xRange=mainParamRange[1], miscParams=miscParams, xSpec=xSpec, ySpec=ySpec, fit=fit, tgt=tgt) - ys = self._denormalizeValues(ys, ySpec, fit, tgt) + def _calcPlotPoints(self, mainInput, miscInputs, xSpec, ySpec, src, tgt): + mainParamRange, miscParams = self._normalizeInputs( + mainInput=mainInput, miscInputs=miscInputs, + src=src, tgt=tgt) + mainParamRange, miscParams = self._limitParams( + mainParamRange=mainParamRange, miscParams=miscParams, + src=src, tgt=tgt) + xs, ys = self._getPoints( + xRange=mainParamRange[1], miscParams=miscParams, + xSpec=xSpec, ySpec=ySpec, src=src, tgt=tgt) + ys = self._denormalizeValues(values=ys, axisSpec=ySpec, src=src, tgt=tgt) # Sometimes x denormalizer may fail (e.g. during conversion of 0 ship speed to %). # If both inputs and outputs are in %, do some extra processing to at least have - # proper graph which shows that fit has the same value over whole specified - # relative parameter range + # proper graph which shows the same value over whole specified relative parameter + # range try: - xs = self._denormalizeValues(xs, xSpec, fit, tgt) + xs = self._denormalizeValues(values=xs, axisSpec=xSpec, src=src, tgt=tgt) except ZeroDivisionError: if mainInput.unit == xSpec.unit == '%' and len(set(floatUnerr(y) for y in ys)) == 1: xs = [min(mainInput.value), max(mainInput.value)] @@ -163,11 +169,11 @@ class FitGraph(metaclass=ABCMeta): _normalizers = {} - def _normalizeInputs(self, mainInput, miscInputs, fit, tgt): + def _normalizeInputs(self, mainInput, miscInputs, src, tgt): key = (mainInput.handle, mainInput.unit) if key in self._normalizers: normalizer = self._normalizers[key] - mainParamRange = (mainInput.handle, tuple(normalizer(v, fit, tgt) for v in mainInput.value)) + mainParamRange = (mainInput.handle, tuple(normalizer(v, src, tgt) for v in mainInput.value)) else: mainParamRange = (mainInput.handle, mainInput.value) miscParams = [] @@ -175,7 +181,7 @@ class FitGraph(metaclass=ABCMeta): key = (miscInput.handle, miscInput.unit) if key in self._normalizers: normalizer = self._normalizers[key] - miscParam = (miscInput.handle, normalizer(miscInput.value, fit, tgt)) + miscParam = (miscInput.handle, normalizer(miscInput.value, src, tgt)) else: miscParam = (miscInput.handle, miscInput.value) miscParams.append(miscParam) @@ -183,7 +189,7 @@ class FitGraph(metaclass=ABCMeta): _limiters = {} - def _limitParams(self, mainParamRange, miscParams, fit, tgt): + def _limitParams(self, mainParamRange, miscParams, src, tgt): def limitToRange(val, limitRange): if val is None: @@ -195,7 +201,7 @@ class FitGraph(metaclass=ABCMeta): mainHandle, mainValue = mainParamRange if mainHandle in self._limiters: limiter = self._limiters[mainHandle] - newMainParamRange = (mainHandle, tuple(limitToRange(v, limiter(fit, tgt)) for v in mainValue)) + newMainParamRange = (mainHandle, tuple(limitToRange(v, limiter(src, tgt)) for v in mainValue)) else: newMainParamRange = mainParamRange newMiscParams = [] @@ -203,7 +209,7 @@ class FitGraph(metaclass=ABCMeta): miscHandle, miscValue = miscParam if miscHandle in self._limiters: limiter = self._limiters[miscHandle] - newMiscParam = (miscHandle, limitToRange(miscValue, limiter(fit, tgt))) + newMiscParam = (miscHandle, limitToRange(miscValue, limiter(src, tgt))) newMiscParams.append(newMiscParam) else: newMiscParams.append(miscParam) @@ -211,20 +217,20 @@ class FitGraph(metaclass=ABCMeta): _getters = {} - def _getPoints(self, xRange, miscParams, xSpec, ySpec, fit, tgt): + def _getPoints(self, xRange, miscParams, xSpec, ySpec, src, tgt): try: getterClass = self._getters[(xSpec.handle, ySpec.handle)] except KeyError: return [], [] else: getter = getterClass(graph=self) - return getter.getRange(xRange=xRange, miscParams=miscParams, fit=fit, tgt=tgt) + return getter.getRange(xRange=xRange, miscParams=miscParams, src=src, tgt=tgt) _denormalizers = {} - def _denormalizeValues(self, values, axisSpec, fit, tgt): + def _denormalizeValues(self, values, axisSpec, src, tgt): key = (axisSpec.handle, axisSpec.unit) if key in self._denormalizers: denormalizer = self._denormalizers[key] - values = [denormalizer(v, fit, tgt) for v in values] + values = [denormalizer(v, src, tgt) for v in values] return values diff --git a/graphs/data/fitCapRegen/getter.py b/graphs/data/fitCapRegen/getter.py index ae6f5c2b0..66d577a37 100644 --- a/graphs/data/fitCapRegen/getter.py +++ b/graphs/data/fitCapRegen/getter.py @@ -25,12 +25,12 @@ from graphs.data.base import SmoothPointGetter class Time2CapAmountGetter(SmoothPointGetter): - def _getCommonData(self, miscParams, fit, tgt): + def _getCommonData(self, miscParams, src, tgt): return { - 'maxCapAmount': fit.ship.getModifiedItemAttr('capacitorCapacity'), - 'capRegenTime': fit.ship.getModifiedItemAttr('rechargeRate') / 1000} + 'maxCapAmount': src.item.ship.getModifiedItemAttr('capacitorCapacity'), + 'capRegenTime': src.item.ship.getModifiedItemAttr('rechargeRate') / 1000} - def _calculatePoint(self, x, miscParams, fit, tgt, commonData): + def _calculatePoint(self, x, miscParams, src, tgt, commonData): time = x capAmount = calculateCapAmount( maxCapAmount=commonData['maxCapAmount'], @@ -41,12 +41,12 @@ class Time2CapAmountGetter(SmoothPointGetter): class Time2CapRegenGetter(SmoothPointGetter): - def _getCommonData(self, miscParams, fit, tgt): + def _getCommonData(self, miscParams, src, tgt): return { - 'maxCapAmount': fit.ship.getModifiedItemAttr('capacitorCapacity'), - 'capRegenTime': fit.ship.getModifiedItemAttr('rechargeRate') / 1000} + 'maxCapAmount': src.item.ship.getModifiedItemAttr('capacitorCapacity'), + 'capRegenTime': src.item.ship.getModifiedItemAttr('rechargeRate') / 1000} - def _calculatePoint(self, x, miscParams, fit, tgt, commonData): + def _calculatePoint(self, x, miscParams, src, tgt, commonData): time = x capAmount = calculateCapAmount( maxCapAmount=commonData['maxCapAmount'], @@ -62,19 +62,19 @@ class Time2CapRegenGetter(SmoothPointGetter): # Useless, but valid combination of x and y class CapAmount2CapAmountGetter(SmoothPointGetter): - def _calculatePoint(self, x, miscParams, fit, tgt, commonData): + def _calculatePoint(self, x, miscParams, src, tgt, commonData): capAmount = x return capAmount class CapAmount2CapRegenGetter(SmoothPointGetter): - def _getCommonData(self, miscParams, fit, tgt): + def _getCommonData(self, miscParams, src, tgt): return { - 'maxCapAmount': fit.ship.getModifiedItemAttr('capacitorCapacity'), - 'capRegenTime': fit.ship.getModifiedItemAttr('rechargeRate') / 1000} + 'maxCapAmount': src.item.ship.getModifiedItemAttr('capacitorCapacity'), + 'capRegenTime': src.item.ship.getModifiedItemAttr('rechargeRate') / 1000} - def _calculatePoint(self, x, miscParams, fit, tgt, commonData): + def _calculatePoint(self, x, miscParams, src, tgt, commonData): capAmount = x capRegen = calculateCapRegen( maxCapAmount=commonData['maxCapAmount'], diff --git a/graphs/data/fitCapRegen/graph.py b/graphs/data/fitCapRegen/graph.py index 1933fb20c..2774a8c49 100644 --- a/graphs/data/fitCapRegen/graph.py +++ b/graphs/data/fitCapRegen/graph.py @@ -41,13 +41,13 @@ class FitCapRegenGraph(FitGraph): # Calculation stuff _normalizers = { - ('capAmount', '%'): lambda v, fit, tgt: v / 100 * fit.ship.getModifiedItemAttr('capacitorCapacity')} + ('capAmount', '%'): lambda v, src, tgt: v / 100 * src.item.ship.getModifiedItemAttr('capacitorCapacity')} _limiters = { - 'capAmount': lambda fit, tgt: (0, fit.ship.getModifiedItemAttr('capacitorCapacity'))} + 'capAmount': lambda src, tgt: (0, src.item.ship.getModifiedItemAttr('capacitorCapacity'))} _getters = { ('time', 'capAmount'): Time2CapAmountGetter, ('time', 'capRegen'): Time2CapRegenGetter, ('capAmount', 'capAmount'): CapAmount2CapAmountGetter, ('capAmount', 'capRegen'): CapAmount2CapRegenGetter} _denormalizers = { - ('capAmount', '%'): lambda v, fit, tgt: v * 100 / fit.ship.getModifiedItemAttr('capacitorCapacity')} + ('capAmount', '%'): lambda v, src, tgt: v * 100 / src.item.ship.getModifiedItemAttr('capacitorCapacity')} diff --git a/graphs/data/fitDamageStats/cache/projected.py b/graphs/data/fitDamageStats/cache/projected.py index f1cc85d88..9e450ca52 100644 --- a/graphs/data/fitDamageStats/cache/projected.py +++ b/graphs/data/fitDamageStats/cache/projected.py @@ -31,15 +31,15 @@ MobileProjData = namedtuple('MobileProjData', ('boost', 'optimal', 'falloff', 's class ProjectedDataCache(FitDataCache): - def getProjModData(self, fit): + def getProjModData(self, src): try: - projectedData = self._data[fit.ID]['modules'] + projectedData = self._data[src.item.ID]['modules'] except KeyError: # Format of items for both: (boost strength, optimal, falloff, stacking group, resistance attr ID) webMods = [] tpMods = [] - projectedData = self._data.setdefault(fit.ID, {})['modules'] = (webMods, tpMods) - for mod in fit.modules: + projectedData = self._data.setdefault(src.item.ID, {})['modules'] = (webMods, tpMods) + for mod in src.item.modules: if mod.state <= FittingModuleState.ONLINE: continue for webEffectName in ('remoteWebifierFalloff', 'structureModuleEffectStasisWebifier'): @@ -53,7 +53,7 @@ class ProjectedDataCache(FitDataCache): if 'doomsdayAOEWeb' in mod.item.effects: webMods.append(ModProjData( mod.getModifiedItemAttr('speedFactor'), - max(0, (mod.maxRange or 0) + mod.getModifiedItemAttr('doomsdayAOERange') - fit.ship.getModifiedItemAttr('radius')), + max(0, (mod.maxRange or 0) + mod.getModifiedItemAttr('doomsdayAOERange') - src.getRadius()), mod.falloff or 0, 'default', getResistanceAttrID(modifyingItem=mod, effect=mod.item.effects['doomsdayAOEWeb']))) @@ -68,21 +68,21 @@ class ProjectedDataCache(FitDataCache): if 'doomsdayAOEPaint' in mod.item.effects: tpMods.append(ModProjData( mod.getModifiedItemAttr('signatureRadiusBonus'), - max(0, (mod.maxRange or 0) + mod.getModifiedItemAttr('doomsdayAOERange') - fit.ship.getModifiedItemAttr('radius')), + max(0, (mod.maxRange or 0) + mod.getModifiedItemAttr('doomsdayAOERange') - src.getRadius()), mod.falloff or 0, 'default', getResistanceAttrID(modifyingItem=mod, effect=mod.item.effects['doomsdayAOEPaint']))) return projectedData - def getProjDroneData(self, fit): + def getProjDroneData(self, src): try: - projectedData = self._data[fit.ID]['drones'] + projectedData = self._data[src.item.ID]['drones'] except KeyError: # Format of items for both: (boost strength, optimal, falloff, stacking group, resistance attr ID, drone speed, drone radius) webDrones = [] tpDrones = [] - projectedData = self._data.setdefault(fit.ID, {})['drones'] = (webDrones, tpDrones) - for drone in fit.drones: + projectedData = self._data.setdefault(src.item.ID, {})['drones'] = (webDrones, tpDrones) + for drone in src.item.drones: if drone.amountActive <= 0: continue if 'remoteWebifierEntity' in drone.item.effects: @@ -105,15 +105,15 @@ class ProjectedDataCache(FitDataCache): drone.getModifiedItemAttr('radius')),)) return projectedData - def getProjFighterData(self, fit): + def getProjFighterData(self, src): try: - projectedData = self._data[fit.ID]['fighters'] + projectedData = self._data[src.item.ID]['fighters'] except KeyError: # Format of items for both: (boost strength, optimal, falloff, stacking group, resistance attr ID, fighter speed, fighter radius) webFighters = [] tpFighters = [] - projectedData = self._data.setdefault(fit.ID, {})['fighters'] = (webFighters, tpFighters) - for fighter in fit.fighters: + projectedData = self._data.setdefault(src.item.ID, {})['fighters'] = (webFighters, tpFighters) + for fighter in src.item.fighters: if not fighter.active: continue for ability in fighter.abilities: diff --git a/graphs/data/fitDamageStats/cache/time.py b/graphs/data/fitDamageStats/cache/time.py index a7721023b..21bcbb3d9 100644 --- a/graphs/data/fitDamageStats/cache/time.py +++ b/graphs/data/fitDamageStats/cache/time.py @@ -29,45 +29,45 @@ from graphs.data.base import FitDataCache class TimeCache(FitDataCache): # Whole data getters - def getDpsData(self, fit): + def getDpsData(self, src): """Return DPS data in {time: {key: dps}} format.""" - return self._data[fit.ID]['finalDps'] + return self._data[src.item.ID]['finalDps'] - def getVolleyData(self, fit): + def getVolleyData(self, src): """Return volley data in {time: {key: volley}} format.""" - return self._data[fit.ID]['finalVolley'] + return self._data[src.item.ID]['finalVolley'] - def getDmgData(self, fit): + def getDmgData(self, src): """Return inflicted damage data in {time: {key: damage}} format.""" - return self._data[fit.ID]['finalDmg'] + return self._data[src.item.ID]['finalDmg'] # Specific data point getters - def getDpsDataPoint(self, fit, time): + def getDpsDataPoint(self, src, time): """Get DPS data by specified time in {key: dps} format.""" - return self._getDataPoint(fit, time, self.getDpsData) + return self._getDataPoint(src=src, time=time, dataFunc=self.getDpsData) - def getVolleyDataPoint(self, fit, time): + def getVolleyDataPoint(self, src, time): """Get volley data by specified time in {key: volley} format.""" - return self._getDataPoint(fit, time, self.getVolleyData) + return self._getDataPoint(src=src, time=time, dataFunc=self.getVolleyData) - def getDmgDataPoint(self, fit, time): + def getDmgDataPoint(self, src, time): """Get inflicted damage data by specified time in {key: dmg} format.""" - return self._getDataPoint(fit, time, self.getDmgData) + return self._getDataPoint(src=src, time=time, dataFunc=self.getDmgData) # Preparation functions - def prepareDpsData(self, fit, maxTime): - self._prepareDpsVolleyData(fit, maxTime) + def prepareDpsData(self, src, maxTime): + self._prepareDpsVolleyData(src=src, maxTime=maxTime) - def prepareVolleyData(self, fit, maxTime): - self._prepareDpsVolleyData(fit, maxTime) + def prepareVolleyData(self, src, maxTime): + self._prepareDpsVolleyData(src=src, maxTime=maxTime) - def prepareDmgData(self, fit, maxTime): + def prepareDmgData(self, src, maxTime): # Time is none means that time parameter has to be ignored, # we do not need cache for that if maxTime is None: return - self._generateInternalForm(fit, maxTime) - fitCache = self._data[fit.ID] + self._generateInternalForm(src=src, maxTime=maxTime) + fitCache = self._data[src.item.ID] # Final cache has been generated already, don't do anything if 'finalDmg' in fitCache: return @@ -93,13 +93,13 @@ class TimeCache(FitDataCache): del fitCache['internalDmg'] # Private stuff - def _prepareDpsVolleyData(self, fit, maxTime): + def _prepareDpsVolleyData(self, src, maxTime): # Time is none means that time parameter has to be ignored, # we do not need cache for that if maxTime is None: return True - self._generateInternalForm(fit, maxTime) - fitCache = self._data[fit.ID] + self._generateInternalForm(src=src, maxTime=maxTime) + fitCache = self._data[src.item.ID] # Final cache has been generated already, don't do anything if 'finalDps' in fitCache and 'finalVolley' in fitCache: return @@ -147,10 +147,10 @@ class TimeCache(FitDataCache): finalDpsCache[time] = timeDpsData finalVolleyCache[time] = timeVolleyData - def _generateInternalForm(self, fit, maxTime): - if self._isTimeCacheValid(fit, maxTime): + def _generateInternalForm(self, src, maxTime): + if self._isTimeCacheValid(src=src, maxTime=maxTime): return - fitCache = self._data[fit.ID] = {'maxTime': maxTime} + fitCache = self._data[src.item.ID] = {'maxTime': maxTime} intCacheDpsVolley = fitCache['internalDpsVolley'] = {} intCacheDmg = fitCache['internalDmg'] = {} @@ -173,7 +173,7 @@ class TimeCache(FitDataCache): intCacheDmg.setdefault(ddKey, {})[addedTime] = addedDmg # Modules - for mod in fit.modules: + for mod in src.item.modules: if not mod.isDealingDamage(): continue cycleParams = mod.getCycleParameters(reloadOverride=True) @@ -196,7 +196,7 @@ class TimeCache(FitDataCache): break currentTime += cycleTimeMs / 1000 + inactiveTimeMs / 1000 # Drones - for drone in fit.drones: + for drone in src.item.drones: if not drone.isDealingDamage(): continue cycleParams = drone.getCycleParameters(reloadOverride=True) @@ -214,7 +214,7 @@ class TimeCache(FitDataCache): break currentTime += cycleTimeMs / 1000 + inactiveTimeMs / 1000 # Fighters - for fighter in fit.fighters: + for fighter in src.item.fighters: if not fighter.isDealingDamage(): continue cycleParams = fighter.getCycleParametersPerEffectOptimizedDps(reloadOverride=True) @@ -236,15 +236,15 @@ class TimeCache(FitDataCache): break currentTime += cycleTimeMs / 1000 + inactiveTimeMs / 1000 - def _isTimeCacheValid(self, fit, maxTime): + def _isTimeCacheValid(self, src, maxTime): try: - cacheMaxTime = self._data[fit.ID]['maxTime'] + cacheMaxTime = self._data[src.item.ID]['maxTime'] except KeyError: return False return maxTime <= cacheMaxTime - def _getDataPoint(self, fit, time, dataFunc): - data = dataFunc(fit) + def _getDataPoint(self, src, time, dataFunc): + data = dataFunc(src) timesBefore = [t for t in data if floatUnerr(t) <= floatUnerr(time)] try: time = max(timesBefore) diff --git a/graphs/data/fitDamageStats/calc/application.py b/graphs/data/fitDamageStats/calc/application.py index 9568e5f9b..afcf612be 100644 --- a/graphs/data/fitDamageStats/calc/application.py +++ b/graphs/data/fitDamageStats/calc/application.py @@ -22,22 +22,20 @@ import math from functools import lru_cache from eos.const import FittingHardpoint -from eos.saveddata.fit import Fit from eos.utils.float import floatUnerr -from graphs.data.fitDamageStats.helper import getTgtRadius from service.const import GraphDpsDroneMode from service.settings import GraphSettings -def getApplicationPerKey(fit, tgt, atkSpeed, atkAngle, distance, tgtSpeed, tgtAngle, tgtSigRadius): +def getApplicationPerKey(src, tgt, atkSpeed, atkAngle, distance, tgtSpeed, tgtAngle, tgtSigRadius): applicationMap = {} - for mod in fit.modules: + for mod in src.item.modules: if not mod.isDealingDamage(): continue if mod.hardpoint == FittingHardpoint.TURRET: applicationMap[mod] = getTurretMult( mod=mod, - fit=fit, + src=src, tgt=tgt, atkSpeed=atkSpeed, atkAngle=atkAngle, @@ -48,7 +46,7 @@ def getApplicationPerKey(fit, tgt, atkSpeed, atkAngle, distance, tgtSpeed, tgtAn elif mod.hardpoint == FittingHardpoint.MISSILE: applicationMap[mod] = getLauncherMult( mod=mod, - fit=fit, + src=src, distance=distance, tgtSpeed=tgtSpeed, tgtSigRadius=tgtSigRadius) @@ -59,14 +57,14 @@ def getApplicationPerKey(fit, tgt, atkSpeed, atkAngle, distance, tgtSpeed, tgtAn elif mod.item.group.name == 'Missile Launcher Bomb': applicationMap[mod] = getBombMult( mod=mod, - fit=fit, + src=src, tgt=tgt, distance=distance, tgtSigRadius=tgtSigRadius) elif mod.item.group.name == 'Structure Guided Bomb Launcher': applicationMap[mod] = getGuidedBombMult( mod=mod, - fit=fit, + src=src, distance=distance, tgtSigRadius=tgtSigRadius) elif mod.item.group.name in ('Super Weapon', 'Structure Doomsday Weapon'): @@ -75,12 +73,12 @@ def getApplicationPerKey(fit, tgt, atkSpeed, atkAngle, distance, tgtSpeed, tgtAn tgt=tgt, distance=distance, tgtSigRadius=tgtSigRadius) - for drone in fit.drones: + for drone in src.item.drones: if not drone.isDealingDamage(): continue applicationMap[drone] = getDroneMult( drone=drone, - fit=fit, + src=src, tgt=tgt, atkSpeed=atkSpeed, atkAngle=atkAngle, @@ -88,7 +86,7 @@ def getApplicationPerKey(fit, tgt, atkSpeed, atkAngle, distance, tgtSpeed, tgtAn tgtSpeed=tgtSpeed, tgtAngle=tgtAngle, tgtSigRadius=tgtSigRadius) - for fighter in fit.fighters: + for fighter in src.item.fighters: if not fighter.isDealingDamage(): continue for ability in fighter.abilities: @@ -97,7 +95,7 @@ def getApplicationPerKey(fit, tgt, atkSpeed, atkAngle, distance, tgtSpeed, tgtAn applicationMap[(fighter, ability.effectID)] = getFighterAbilityMult( fighter=fighter, ability=ability, - fit=fit, + src=src, distance=distance, tgtSpeed=tgtSpeed, tgtSigRadius=tgtSigRadius) @@ -108,11 +106,11 @@ def getApplicationPerKey(fit, tgt, atkSpeed, atkAngle, distance, tgtSpeed, tgtAn # Item application multiplier calculation -def getTurretMult(mod, fit, tgt, atkSpeed, atkAngle, distance, tgtSpeed, tgtAngle, tgtSigRadius): +def getTurretMult(mod, src, tgt, atkSpeed, atkAngle, distance, tgtSpeed, tgtAngle, tgtSigRadius): cth = _calcTurretChanceToHit( atkSpeed=atkSpeed, atkAngle=atkAngle, - atkRadius=fit.ship.getModifiedItemAttr('radius'), + atkRadius=src.getRadius(), atkOptimalRange=mod.maxRange, atkFalloffRange=mod.falloff, atkTracking=mod.getModifiedItemAttr('trackingSpeed'), @@ -120,17 +118,17 @@ def getTurretMult(mod, fit, tgt, atkSpeed, atkAngle, distance, tgtSpeed, tgtAngl distance=distance, tgtSpeed=tgtSpeed, tgtAngle=tgtAngle, - tgtRadius=getTgtRadius(tgt), + tgtRadius=tgt.getRadius(), tgtSigRadius=tgtSigRadius) mult = _calcTurretMult(cth) return mult -def getLauncherMult(mod, fit, distance, tgtSpeed, tgtSigRadius): +def getLauncherMult(mod, src, distance, tgtSpeed, tgtSigRadius): modRange = mod.maxRange if modRange is None: return 0 - if distance is not None and distance + fit.ship.getModifiedItemAttr('radius') > modRange: + if distance is not None and distance + src.getRadius() > modRange: return 0 mult = _calcMissileFactor( atkEr=mod.getModifiedChargeAttr('aoeCloudSize'), @@ -157,8 +155,8 @@ def getDoomsdayMult(mod, tgt, distance, tgtSigRadius): return 0 # Single-target titan DDs are vs capitals only if {'superWeaponAmarr', 'superWeaponCaldari', 'superWeaponGallente', 'superWeaponMinmatar'}.intersection(mod.item.effects): - # Disallow only against subcap fits, allow against cap fits and tgt profiles - if isinstance(tgt, Fit) and not tgt.ship.item.requiresSkill('Capital Ships'): + # Disallow only against subcaps, allow against caps and tgt profiles + if tgt.isFit and not tgt.item.ship.item.requiresSkill('Capital Ships'): return 0 damageSig = mod.getModifiedItemAttr('doomsdayDamageRadius') or mod.getModifiedItemAttr('signatureRadius') if not damageSig: @@ -166,13 +164,13 @@ def getDoomsdayMult(mod, tgt, distance, tgtSigRadius): return min(1, tgtSigRadius / damageSig) -def getBombMult(mod, fit, tgt, distance, tgtSigRadius): +def getBombMult(mod, src, tgt, distance, tgtSigRadius): modRange = mod.maxRange if modRange is None: return 0 blastRadius = mod.getModifiedChargeAttr('explosionRange') - atkRadius = fit.ship.getModifiedItemAttr('radius') - tgtRadius = getTgtRadius(tgt) + atkRadius = src.getRadius() + tgtRadius = tgt.getRadius() # Bomb starts in the center of the ship # Also here we assume that it affects target as long as blast # touches its surface, not center - I did not check this @@ -185,11 +183,11 @@ def getBombMult(mod, fit, tgt, distance, tgtSigRadius): tgtSigRadius=tgtSigRadius) -def getGuidedBombMult(mod, fit, distance, tgtSigRadius): +def getGuidedBombMult(mod, src, distance, tgtSigRadius): modRange = mod.maxRange if modRange is None: return 0 - if distance is not None and distance > modRange - fit.ship.getModifiedItemAttr('radius'): + if distance is not None and distance > modRange - src.getRadius(): return 0 eR = mod.getModifiedChargeAttr('aoeCloudSize') if eR == 0: @@ -198,8 +196,8 @@ def getGuidedBombMult(mod, fit, distance, tgtSigRadius): return min(1, tgtSigRadius / eR) -def getDroneMult(drone, fit, tgt, atkSpeed, atkAngle, distance, tgtSpeed, tgtAngle, tgtSigRadius): - if distance is not None and distance > fit.extraAttributes['droneControlRange']: +def getDroneMult(drone, src, tgt, atkSpeed, atkAngle, distance, tgtSpeed, tgtAngle, tgtSigRadius): + if distance is not None and distance > src.item.extraAttributes['droneControlRange']: return 0 droneSpeed = drone.getModifiedItemAttr('maxVelocity') # Hard to simulate drone behavior, so assume chance to hit is 1 for mobile drones @@ -219,8 +217,8 @@ def getDroneMult(drone, fit, tgt, atkSpeed, atkAngle, distance, tgtSpeed, tgtAng cthDistance = None else: # As distance is ship surface to ship surface, we adjust it according - # to attacker fit's radiuses to have drone surface to ship surface distance - cthDistance = distance + fit.ship.getModifiedItemAttr('radius') - droneRadius + # to attacker ship's radiuses to have drone surface to ship surface distance + cthDistance = distance + src.getRadius() - droneRadius cth = _calcTurretChanceToHit( atkSpeed=min(atkSpeed, droneSpeed), atkAngle=atkAngle, @@ -232,13 +230,13 @@ def getDroneMult(drone, fit, tgt, atkSpeed, atkAngle, distance, tgtSpeed, tgtAng distance=cthDistance, tgtSpeed=tgtSpeed, tgtAngle=tgtAngle, - tgtRadius=getTgtRadius(tgt), + tgtRadius=tgt.getRadius(), tgtSigRadius=tgtSigRadius) mult = _calcTurretMult(cth) return mult -def getFighterAbilityMult(fighter, ability, fit, distance, tgtSpeed, tgtSigRadius): +def getFighterAbilityMult(fighter, ability, src, distance, tgtSpeed, tgtSigRadius): fighterSpeed = fighter.getModifiedItemAttr('maxVelocity') attrPrefix = ability.attrPrefix # It's bomb attack @@ -257,7 +255,7 @@ def getFighterAbilityMult(fighter, ability, fit, distance, tgtSpeed, tgtSigRadiu if distance is None: rangeFactorDistance = None else: - rangeFactorDistance = distance + fit.ship.getModifiedItemAttr('radius') - fighter.getModifiedItemAttr('radius') + rangeFactorDistance = distance + src.getRadius() - fighter.getModifiedItemAttr('radius') rangeFactor = _calcRangeFactor( atkOptimalRange=fighter.getModifiedItemAttr('{}RangeOptimal'.format(attrPrefix)) or fighter.getModifiedItemAttr('{}Range'.format(attrPrefix)), atkFalloffRange=fighter.getModifiedItemAttr('{}RangeFalloff'.format(attrPrefix)), diff --git a/graphs/data/fitDamageStats/calc/projected.py b/graphs/data/fitDamageStats/calc/projected.py index 809802490..3f7821082 100644 --- a/graphs/data/fitDamageStats/calc/projected.py +++ b/graphs/data/fitDamageStats/calc/projected.py @@ -20,19 +20,17 @@ import math -from eos.saveddata.fit import Fit from eos.utils.float import floatUnerr -from graphs.data.fitDamageStats.helper import getTgtMaxVelocity, getTgtSigRadius from service.const import GraphDpsDroneMode from service.settings import GraphSettings from .application import _calcRangeFactor -def getWebbedSpeed(fit, tgt, currentUnwebbedSpeed, webMods, webDrones, webFighters, distance): - # Can slow down non-immune fits and target profiles - if isinstance(tgt, Fit) and tgt.ship.getModifiedItemAttr('disallowOffensiveModifiers'): +def getWebbedSpeed(src, tgt, currentUnwebbedSpeed, webMods, webDrones, webFighters, distance): + # Can slow down non-immune ships and target profiles + if tgt.isFit and tgt.item.ship.getModifiedItemAttr('disallowOffensiveModifiers'): return currentUnwebbedSpeed - maxUnwebbedSpeed = getTgtMaxVelocity(tgt) + maxUnwebbedSpeed = tgt.getMaxVelocity() try: speedRatio = currentUnwebbedSpeed / maxUnwebbedSpeed except ZeroDivisionError: @@ -47,15 +45,15 @@ def getWebbedSpeed(fit, tgt, currentUnwebbedSpeed, webMods, webDrones, webFighte distance=distance) if appliedBoost: appliedMultipliers.setdefault(wData.stackingGroup, []).append((1 + appliedBoost / 100, wData.resAttrID)) - maxWebbedSpeed = getTgtMaxVelocity(tgt, extraMultipliers=appliedMultipliers) + maxWebbedSpeed = tgt.getMaxVelocity(extraMultipliers=appliedMultipliers) currentWebbedSpeed = maxWebbedSpeed * speedRatio # Drones and fighters mobileWebs = [] mobileWebs.extend(webFighters) # Drones have range limit - if distance is None or distance <= fit.extraAttributes['droneControlRange']: + if distance is None or distance <= src.extraAttributes['droneControlRange']: mobileWebs.extend(webDrones) - atkRadius = fit.ship.getModifiedItemAttr('radius') + atkRadius = src.getRadius() # As mobile webs either follow the target or stick to the attacking ship, # if target is within mobile web optimal - it can be applied unconditionally longEnoughMws = [mw for mw in mobileWebs if distance is None or distance <= mw.optimal - atkRadius + mw.radius] @@ -63,7 +61,7 @@ def getWebbedSpeed(fit, tgt, currentUnwebbedSpeed, webMods, webDrones, webFighte for mwData in longEnoughMws: appliedMultipliers.setdefault(mwData.stackingGroup, []).append((1 + mwData.boost / 100, mwData.resAttrID)) mobileWebs.remove(mwData) - maxWebbedSpeed = getTgtMaxVelocity(tgt, extraMultipliers=appliedMultipliers) + maxWebbedSpeed = tgt.getMaxVelocity(extraMultipliers=appliedMultipliers) currentWebbedSpeed = maxWebbedSpeed * speedRatio # Apply remaining webs, from fastest to slowest droneOpt = GraphSettings.getInstance().get('mobileDroneMode') @@ -87,17 +85,17 @@ def getWebbedSpeed(fit, tgt, currentUnwebbedSpeed, webMods, webDrones, webFighte distance=rangeFactorDistance) appliedMultipliers.setdefault(mwData.stackingGroup, []).append((1 + appliedMwBoost / 100, mwData.resAttrID)) mobileWebs.remove(mwData) - maxWebbedSpeed = getTgtMaxVelocity(tgt, extraMultipliers=appliedMultipliers) + maxWebbedSpeed = tgt.getMaxVelocity(extraMultipliers=appliedMultipliers) currentWebbedSpeed = maxWebbedSpeed * speedRatio # Ensure consistent results - round off a little to avoid float errors return floatUnerr(currentWebbedSpeed) -def getTpMult(fit, tgt, tgtSpeed, tpMods, tpDrones, tpFighters, distance): - # Can blow non-immune fits and target profiles - if isinstance(tgt, Fit) and tgt.ship.getModifiedItemAttr('disallowOffensiveModifiers'): +def getTpMult(src, tgt, tgtSpeed, tpMods, tpDrones, tpFighters, distance): + # Can blow non-immune ships and target profiles + if tgt.isFit and tgt.item.ship.getModifiedItemAttr('disallowOffensiveModifiers'): return 1 - untpedSig = getTgtSigRadius(tgt) + untpedSig = tgt.getSigRadius() # Modules appliedMultipliers = {} for tpData in tpMods: @@ -111,10 +109,10 @@ def getTpMult(fit, tgt, tgtSpeed, tpMods, tpDrones, tpFighters, distance): mobileTps = [] mobileTps.extend(tpFighters) # Drones have range limit - if distance is None or distance <= fit.extraAttributes['droneControlRange']: + if distance is None or distance <= src.item.extraAttributes['droneControlRange']: mobileTps.extend(tpDrones) droneOpt = GraphSettings.getInstance().get('mobileDroneMode') - atkRadius = fit.ship.getModifiedItemAttr('radius') + atkRadius = src.getRadius() for mtpData in mobileTps: # Faster than target or set to follow it - apply full TP if (droneOpt == GraphDpsDroneMode.auto and mtpData.speed >= tgtSpeed) or droneOpt == GraphDpsDroneMode.followTarget: @@ -130,7 +128,7 @@ def getTpMult(fit, tgt, tgtSpeed, tpMods, tpDrones, tpFighters, distance): atkFalloffRange=mtpData.falloff, distance=rangeFactorDistance) appliedMultipliers.setdefault(mtpData.stackingGroup, []).append((1 + appliedMtpBoost / 100, mtpData.resAttrID)) - tpedSig = getTgtSigRadius(tgt, extraMultipliers=appliedMultipliers) + tpedSig = tgt.getSigRadius(extraMultipliers=appliedMultipliers) if tpedSig == math.inf and untpedSig == math.inf: return 1 mult = tpedSig / untpedSig diff --git a/graphs/data/fitDamageStats/getter.py b/graphs/data/fitDamageStats/getter.py index cd70fe798..96b57c0cf 100644 --- a/graphs/data/fitDamageStats/getter.py +++ b/graphs/data/fitDamageStats/getter.py @@ -25,7 +25,6 @@ from graphs.data.base import PointGetter, SmoothPointGetter from service.settings import GraphSettings from .calc.application import getApplicationPerKey from .calc.projected import getTpMult, getWebbedSpeed -from .helper import getTgtSigRadius def applyDamage(dmgMap, applicationMap): @@ -38,88 +37,88 @@ def applyDamage(dmgMap, applicationMap): # Y mixins class YDpsMixin: - def _getDamagePerKey(self, fit, time): + def _getDamagePerKey(self, src, time): # Use data from time cache if time was not specified if time is not None: - return self._getTimeCacheDataPoint(fit=fit, time=time) + return self._getTimeCacheDataPoint(src=src, time=time) # Compose map ourselves using current fit settings if time is not specified dpsMap = {} defaultSpoolValue = eos.config.settings['globalDefaultSpoolupPercentage'] - for mod in fit.modules: + for mod in src.item.modules: if not mod.isDealingDamage(): continue dpsMap[mod] = mod.getDps(spoolOptions=SpoolOptions(SpoolType.SCALE, defaultSpoolValue, False)) - for drone in fit.drones: + for drone in src.item.drones: if not drone.isDealingDamage(): continue dpsMap[drone] = drone.getDps() - for fighter in fit.fighters: + for fighter in src.item.fighters: if not fighter.isDealingDamage(): continue for effectID, effectDps in fighter.getDpsPerEffect().items(): dpsMap[(fighter, effectID)] = effectDps return dpsMap - def _prepareTimeCache(self, fit, maxTime): - self.graph._timeCache.prepareDpsData(fit=fit, maxTime=maxTime) + def _prepareTimeCache(self, src, maxTime): + self.graph._timeCache.prepareDpsData(src=src, maxTime=maxTime) - def _getTimeCacheData(self, fit): - return self.graph._timeCache.getDpsData(fit=fit) + def _getTimeCacheData(self, src): + return self.graph._timeCache.getDpsData(src=src) - def _getTimeCacheDataPoint(self, fit, time): - return self.graph._timeCache.getDpsDataPoint(fit=fit, time=time) + def _getTimeCacheDataPoint(self, src, time): + return self.graph._timeCache.getDpsDataPoint(src=src, time=time) class YVolleyMixin: - def _getDamagePerKey(self, fit, time): + def _getDamagePerKey(self, src, time): # Use data from time cache if time was not specified if time is not None: - return self._getTimeCacheDataPoint(fit=fit, time=time) + return self._getTimeCacheDataPoint(src=src, time=time) # Compose map ourselves using current fit settings if time is not specified volleyMap = {} defaultSpoolValue = eos.config.settings['globalDefaultSpoolupPercentage'] - for mod in fit.modules: + for mod in src.item.modules: if not mod.isDealingDamage(): continue volleyMap[mod] = mod.getVolley(spoolOptions=SpoolOptions(SpoolType.SCALE, defaultSpoolValue, False)) - for drone in fit.drones: + for drone in src.item.drones: if not drone.isDealingDamage(): continue volleyMap[drone] = drone.getVolley() - for fighter in fit.fighters: + for fighter in src.item.fighters: if not fighter.isDealingDamage(): continue for effectID, effectVolley in fighter.getVolleyPerEffect().items(): volleyMap[(fighter, effectID)] = effectVolley return volleyMap - def _prepareTimeCache(self, fit, maxTime): - self.graph._timeCache.prepareVolleyData(fit=fit, maxTime=maxTime) + def _prepareTimeCache(self, src, maxTime): + self.graph._timeCache.prepareVolleyData(src=src, maxTime=maxTime) - def _getTimeCacheData(self, fit): - return self.graph._timeCache.getVolleyData(fit=fit) + def _getTimeCacheData(self, src): + return self.graph._timeCache.getVolleyData(src=src) - def _getTimeCacheDataPoint(self, fit, time): - return self.graph._timeCache.getVolleyDataPoint(fit=fit, time=time) + def _getTimeCacheDataPoint(self, src, time): + return self.graph._timeCache.getVolleyDataPoint(src=src, time=time) class YInflictedDamageMixin: - def _getDamagePerKey(self, fit, time): + def _getDamagePerKey(self, src, time): # Damage inflicted makes no sense without time specified if time is None: raise ValueError - return self._getTimeCacheDataPoint(fit=fit, time=time) + return self._getTimeCacheDataPoint(src=src, time=time) - def _prepareTimeCache(self, fit, maxTime): - self.graph._timeCache.prepareDmgData(fit=fit, maxTime=maxTime) + def _prepareTimeCache(self, src, maxTime): + self.graph._timeCache.prepareDmgData(src=src, maxTime=maxTime) - def _getTimeCacheData(self, fit): - return self.graph._timeCache.getDmgData(fit=fit) + def _getTimeCacheData(self, src): + return self.graph._timeCache.getDmgData(src=src) - def _getTimeCacheDataPoint(self, fit, time): - return self.graph._timeCache.getDmgDataPoint(fit=fit, time=time) + def _getTimeCacheDataPoint(self, src, time): + return self.graph._timeCache.getDmgDataPoint(src=src, time=time) # X mixins @@ -128,28 +127,28 @@ class XDistanceMixin(SmoothPointGetter): _baseResolution = 50 _extraDepth = 2 - def _getCommonData(self, miscParams, fit, tgt): + def _getCommonData(self, miscParams, src, tgt): # Process params into more convenient form miscParamMap = dict(miscParams) # Prepare time cache here because we need to do it only once, # and this function is called once per point info fetch - self._prepareTimeCache(fit=fit, maxTime=miscParamMap['time']) + self._prepareTimeCache(src=src, maxTime=miscParamMap['time']) return { 'applyProjected': GraphSettings.getInstance().get('applyProjected'), 'miscParamMap': miscParamMap, - 'dmgMap': self._getDamagePerKey(fit=fit, time=miscParamMap['time'])} + 'dmgMap': self._getDamagePerKey(src=src, time=miscParamMap['time'])} - def _calculatePoint(self, x, miscParams, fit, tgt, commonData): + def _calculatePoint(self, x, miscParams, src, tgt, commonData): distance = x miscParamMap = commonData['miscParamMap'] tgtSpeed = miscParamMap['tgtSpeed'] - tgtSigRadius = getTgtSigRadius(tgt) + tgtSigRadius = tgt.getSigRadius() if commonData['applyProjected']: - webMods, tpMods = self.graph._projectedCache.getProjModData(fit) - webDrones, tpDrones = self.graph._projectedCache.getProjDroneData(fit) - webFighters, tpFighters = self.graph._projectedCache.getProjFighterData(fit) + webMods, tpMods = self.graph._projectedCache.getProjModData(src) + webDrones, tpDrones = self.graph._projectedCache.getProjDroneData(src) + webFighters, tpFighters = self.graph._projectedCache.getProjFighterData(src) tgtSpeed = getWebbedSpeed( - fit=fit, + src=src, tgt=tgt, currentUnwebbedSpeed=tgtSpeed, webMods=webMods, @@ -157,7 +156,7 @@ class XDistanceMixin(SmoothPointGetter): webFighters=webFighters, distance=distance) tgtSigRadius = tgtSigRadius * getTpMult( - fit=fit, + src=src, tgt=tgt, tgtSpeed=tgtSpeed, tpMods=tpMods, @@ -165,7 +164,7 @@ class XDistanceMixin(SmoothPointGetter): tpFighters=tpFighters, distance=distance) applicationMap = getApplicationPerKey( - fit=fit, + src=src, tgt=tgt, atkSpeed=miscParamMap['atkSpeed'], atkAngle=miscParamMap['atkAngle'], @@ -179,17 +178,17 @@ class XDistanceMixin(SmoothPointGetter): class XTimeMixin(PointGetter): - def _prepareApplicationMap(self, miscParams, fit, tgt): + def _prepareApplicationMap(self, miscParams, src, tgt): # Process params into more convenient form miscParamMap = dict(miscParams) tgtSpeed = miscParamMap['tgtSpeed'] - tgtSigRadius = getTgtSigRadius(tgt) + tgtSigRadius = tgt.getSigRadius() if GraphSettings.getInstance().get('applyProjected'): - webMods, tpMods = self.graph._projectedCache.getProjModData(fit) - webDrones, tpDrones = self.graph._projectedCache.getProjDroneData(fit) - webFighters, tpFighters = self.graph._projectedCache.getProjFighterData(fit) + webMods, tpMods = self.graph._projectedCache.getProjModData(src) + webDrones, tpDrones = self.graph._projectedCache.getProjDroneData(src) + webFighters, tpFighters = self.graph._projectedCache.getProjFighterData(src) tgtSpeed = getWebbedSpeed( - fit=fit, + src=src, tgt=tgt, currentUnwebbedSpeed=tgtSpeed, webMods=webMods, @@ -197,7 +196,7 @@ class XTimeMixin(PointGetter): webFighters=webFighters, distance=miscParamMap['distance']) tgtSigRadius = tgtSigRadius * getTpMult( - fit=fit, + src=src, tgt=tgt, tgtSpeed=tgtSpeed, tpMods=tpMods, @@ -206,7 +205,7 @@ class XTimeMixin(PointGetter): distance=miscParamMap['distance']) # Get all data we need for all times into maps/caches applicationMap = getApplicationPerKey( - fit=fit, + src=src, tgt=tgt, atkSpeed=miscParamMap['atkSpeed'], atkAngle=miscParamMap['atkAngle'], @@ -216,14 +215,14 @@ class XTimeMixin(PointGetter): tgtSigRadius=tgtSigRadius) return applicationMap - def getRange(self, xRange, miscParams, fit, tgt): + def getRange(self, xRange, miscParams, src, tgt): xs = [] ys = [] minTime, maxTime = xRange # Prepare time cache and various shared data - self._prepareTimeCache(fit=fit, maxTime=maxTime) - timeCache = self._getTimeCacheData(fit=fit) - applicationMap = self._prepareApplicationMap(miscParams=miscParams, fit=fit, tgt=tgt) + self._prepareTimeCache(src=src, maxTime=maxTime) + timeCache = self._getTimeCacheData(src=src) + applicationMap = self._prepareApplicationMap(miscParams=miscParams, src=src, tgt=tgt) # Custom iteration for time graph to show all data points currentDmg = None currentTime = None @@ -270,12 +269,12 @@ class XTimeMixin(PointGetter): ys.append(currentDmg or 0) return xs, ys - def getPoint(self, x, miscParams, fit, tgt): + def getPoint(self, x, miscParams, src, tgt): time = x # Prepare time cache and various data - self._prepareTimeCache(fit=fit, maxTime=time) - dmgData = self._getTimeCacheDataPoint(fit=fit, time=time) - applicationMap = self._prepareApplicationMap(miscParams=miscParams, fit=fit, tgt=tgt) + self._prepareTimeCache(src=src, maxTime=time) + dmgData = self._getTimeCacheDataPoint(src=src, time=time) + applicationMap = self._prepareApplicationMap(miscParams=miscParams, src=src, tgt=tgt) y = applyDamage(dmgMap=dmgData, applicationMap=applicationMap).total return y @@ -285,27 +284,27 @@ class XTgtSpeedMixin(SmoothPointGetter): _baseResolution = 50 _extraDepth = 2 - def _getCommonData(self, miscParams, fit, tgt): + def _getCommonData(self, miscParams, src, tgt): # Process params into more convenient form miscParamMap = dict(miscParams) # Prepare time cache here because we need to do it only once, # and this function is called once per point info fetch - self._prepareTimeCache(fit=fit, maxTime=miscParamMap['time']) + self._prepareTimeCache(src=src, maxTime=miscParamMap['time']) return { 'applyProjected': GraphSettings.getInstance().get('applyProjected'), 'miscParamMap': miscParamMap, - 'dmgMap': self._getDamagePerKey(fit=fit, time=miscParamMap['time'])} + 'dmgMap': self._getDamagePerKey(src=src, time=miscParamMap['time'])} - def _calculatePoint(self, x, miscParams, fit, tgt, commonData): + def _calculatePoint(self, x, miscParams, src, tgt, commonData): tgtSpeed = x miscParamMap = commonData['miscParamMap'] - tgtSigRadius = getTgtSigRadius(tgt) + tgtSigRadius = tgt.getSigRadius() if commonData['applyProjected']: - webMods, tpMods = self.graph._projectedCache.getProjModData(fit) - webDrones, tpDrones = self.graph._projectedCache.getProjDroneData(fit) - webFighters, tpFighters = self.graph._projectedCache.getProjFighterData(fit) + webMods, tpMods = self.graph._projectedCache.getProjModData(src) + webDrones, tpDrones = self.graph._projectedCache.getProjDroneData(src) + webFighters, tpFighters = self.graph._projectedCache.getProjFighterData(src) tgtSpeed = getWebbedSpeed( - fit=fit, + src=src, tgt=tgt, currentUnwebbedSpeed=tgtSpeed, webMods=webMods, @@ -313,7 +312,7 @@ class XTgtSpeedMixin(SmoothPointGetter): webFighters=webFighters, distance=miscParamMap['distance']) tgtSigRadius = tgtSigRadius * getTpMult( - fit=fit, + src=src, tgt=tgt, tgtSpeed=tgtSpeed, tpMods=tpMods, @@ -321,7 +320,7 @@ class XTgtSpeedMixin(SmoothPointGetter): tpFighters=tpFighters, distance=miscParamMap['distance']) applicationMap = getApplicationPerKey( - fit=fit, + src=src, tgt=tgt, atkSpeed=miscParamMap['atkSpeed'], atkAngle=miscParamMap['atkAngle'], @@ -338,17 +337,17 @@ class XTgtSigRadiusMixin(SmoothPointGetter): _baseResolution = 50 _extraDepth = 2 - def _getCommonData(self, miscParams, fit, tgt): + def _getCommonData(self, miscParams, src, tgt): # Process params into more convenient form miscParamMap = dict(miscParams) tgtSpeed = miscParamMap['tgtSpeed'] tgtSigMult = 1 if GraphSettings.getInstance().get('applyProjected'): - webMods, tpMods = self.graph._projectedCache.getProjModData(fit) - webDrones, tpDrones = self.graph._projectedCache.getProjDroneData(fit) - webFighters, tpFighters = self.graph._projectedCache.getProjFighterData(fit) + webMods, tpMods = self.graph._projectedCache.getProjModData(src) + webDrones, tpDrones = self.graph._projectedCache.getProjDroneData(src) + webFighters, tpFighters = self.graph._projectedCache.getProjFighterData(src) tgtSpeed = getWebbedSpeed( - fit=fit, + src=src, tgt=tgt, currentUnwebbedSpeed=tgtSpeed, webMods=webMods, @@ -356,7 +355,7 @@ class XTgtSigRadiusMixin(SmoothPointGetter): webFighters=webFighters, distance=miscParamMap['distance']) tgtSigMult = getTpMult( - fit=fit, + src=src, tgt=tgt, tgtSpeed=tgtSpeed, tpMods=tpMods, @@ -365,18 +364,18 @@ class XTgtSigRadiusMixin(SmoothPointGetter): distance=miscParamMap['distance']) # Prepare time cache here because we need to do it only once, # and this function is called once per point info fetch - self._prepareTimeCache(fit=fit, maxTime=miscParamMap['time']) + self._prepareTimeCache(src=src, maxTime=miscParamMap['time']) return { 'miscParamMap': miscParamMap, 'tgtSpeed': tgtSpeed, 'tgtSigMult': tgtSigMult, - 'dmgMap': self._getDamagePerKey(fit=fit, time=miscParamMap['time'])} + 'dmgMap': self._getDamagePerKey(src=src, time=miscParamMap['time'])} - def _calculatePoint(self, x, miscParams, fit, tgt, commonData): + def _calculatePoint(self, x, miscParams, src, tgt, commonData): tgtSigRadius = x miscParamMap = commonData['miscParamMap'] applicationMap = getApplicationPerKey( - fit=fit, + src=src, tgt=tgt, atkSpeed=miscParamMap['atkSpeed'], atkAngle=miscParamMap['atkAngle'], diff --git a/graphs/data/fitDamageStats/graph.py b/graphs/data/fitDamageStats/graph.py index bbf19175f..3e3b6e254 100644 --- a/graphs/data/fitDamageStats/graph.py +++ b/graphs/data/fitDamageStats/graph.py @@ -26,7 +26,6 @@ from .getter import ( Time2DpsGetter, Time2VolleyGetter, Time2InflictedDamageGetter, TgtSpeed2DpsGetter, TgtSpeed2VolleyGetter, TgtSpeed2InflictedDamageGetter, TgtSigRadius2DpsGetter, TgtSigRadius2VolleyGetter, TgtSigRadius2InflictedDamageGetter) -from .helper import getTgtMaxVelocity, getTgtSigRadius class FitDamageStatsGraph(FitGraph): @@ -76,12 +75,12 @@ class FitDamageStatsGraph(FitGraph): # Calculation stuff _normalizers = { - ('distance', 'km'): lambda v, fit, tgt: None if v is None else v * 1000, - ('atkSpeed', '%'): lambda v, fit, tgt: v / 100 * fit.ship.getModifiedItemAttr('maxVelocity'), - ('tgtSpeed', '%'): lambda v, fit, tgt: v / 100 * getTgtMaxVelocity(tgt), - ('tgtSigRad', '%'): lambda v, fit, tgt: v / 100 * getTgtSigRadius(tgt)} + ('distance', 'km'): lambda v, src, tgt: None if v is None else v * 1000, + ('atkSpeed', '%'): lambda v, src, tgt: v / 100 * src.getMaxVelocity(), + ('tgtSpeed', '%'): lambda v, src, tgt: v / 100 * tgt.getMaxVelocity(), + ('tgtSigRad', '%'): lambda v, src, tgt: v / 100 * tgt.getSigRadius()} _limiters = { - 'time': lambda fit, tgt: (0, 2500)} + 'time': lambda src, tgt: (0, 2500)} _getters = { ('distance', 'dps'): Distance2DpsGetter, ('distance', 'volley'): Distance2VolleyGetter, @@ -96,6 +95,6 @@ class FitDamageStatsGraph(FitGraph): ('tgtSigRad', 'volley'): TgtSigRadius2VolleyGetter, ('tgtSigRad', 'damage'): TgtSigRadius2InflictedDamageGetter} _denormalizers = { - ('distance', 'km'): lambda v, fit, tgt: None if v is None else v / 1000, - ('tgtSpeed', '%'): lambda v, fit, tgt: v * 100 / getTgtMaxVelocity(tgt), - ('tgtSigRad', '%'): lambda v, fit, tgt: v * 100 / getTgtSigRadius(tgt)} + ('distance', 'km'): lambda v, src, tgt: None if v is None else v / 1000, + ('tgtSpeed', '%'): lambda v, src, tgt: v * 100 / tgt.getMaxVelocity(), + ('tgtSigRad', '%'): lambda v, src, tgt: v * 100 / tgt.getSigRadius()} diff --git a/graphs/data/fitDamageStats/helper.py b/graphs/data/fitDamageStats/helper.py deleted file mode 100644 index d2fba6ce8..000000000 --- a/graphs/data/fitDamageStats/helper.py +++ /dev/null @@ -1,89 +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 . -# ============================================================================= - - -import math - -from eos.saveddata.fit import Fit -from eos.saveddata.targetProfile import TargetProfile - - -def getTgtMaxVelocity(tgt, extraMultipliers=None): - if isinstance(tgt, Fit): - if extraMultipliers: - maxVelocity = tgt.ship.getModifiedItemAttrWithExtraMods('maxVelocity', extraMultipliers=extraMultipliers) - else: - maxVelocity = tgt.ship.getModifiedItemAttr('maxVelocity') - elif isinstance(tgt, TargetProfile): - maxVelocity = tgt.maxVelocity - if extraMultipliers: - maxVelocity *= _calculateMultiplier(extraMultipliers) - else: - maxVelocity = None - return maxVelocity - - -def getTgtSigRadius(tgt, extraMultipliers=None): - if isinstance(tgt, Fit): - if extraMultipliers: - sigRadius = tgt.ship.getModifiedItemAttrWithExtraMods('signatureRadius', extraMultipliers=extraMultipliers) - else: - sigRadius = tgt.ship.getModifiedItemAttr('signatureRadius') - elif isinstance(tgt, TargetProfile): - sigRadius = tgt.signatureRadius - if extraMultipliers: - sigRadius *= _calculateMultiplier(extraMultipliers) - else: - sigRadius = None - return sigRadius - - -def getTgtRadius(tgt): - if isinstance(tgt, Fit): - radius = tgt.ship.getModifiedItemAttr('radius') - elif isinstance(tgt, TargetProfile): - radius = tgt.radius - else: - radius = None - return radius - - -# Just copy-paste penalization chain calculation code (with some modifications, -# as multipliers arrive in different form) in here to not make actual attribute -# calculations slower than they already are due to extra function calls -def _calculateMultiplier(multipliers): - val = 1 - for penalizedMultipliers in multipliers.values(): - # A quick explanation of how this works: - # 1: Bonuses and penalties are calculated seperately, so we'll have to filter each of them - l1 = [v[0] for v in penalizedMultipliers if v[0] > 1] - l2 = [v[0] for v in penalizedMultipliers if v[0] < 1] - # 2: The most significant bonuses take the smallest penalty, - # This means we'll have to sort - abssort = lambda _val: -abs(_val - 1) - l1.sort(key=abssort) - l2.sort(key=abssort) - # 3: The first module doesn't get penalized at all - # Any module after the first takes penalties according to: - # 1 + (multiplier - 1) * math.exp(- math.pow(i, 2) / 7.1289) - for l in (l1, l2): - for i in range(len(l)): - bonus = l[i] - val *= 1 + (bonus - 1) * math.exp(- i ** 2 / 7.1289) - return val diff --git a/graphs/data/fitMobility/getter.py b/graphs/data/fitMobility/getter.py index fabacd06a..5e0117c7d 100644 --- a/graphs/data/fitMobility/getter.py +++ b/graphs/data/fitMobility/getter.py @@ -25,13 +25,13 @@ from graphs.data.base import SmoothPointGetter class Time2SpeedGetter(SmoothPointGetter): - def _getCommonData(self, miscParams, fit, tgt): + def _getCommonData(self, miscParams, src, tgt): return { - 'maxSpeed': fit.ship.getModifiedItemAttr('maxVelocity'), - 'mass': fit.ship.getModifiedItemAttr('mass'), - 'agility': fit.ship.getModifiedItemAttr('agility')} + 'maxSpeed': src.getMaxVelocity(), + 'mass': src.item.ship.getModifiedItemAttr('mass'), + 'agility': src.item.ship.getModifiedItemAttr('agility')} - def _calculatePoint(self, x, miscParams, fit, tgt, commonData): + def _calculatePoint(self, x, miscParams, src, tgt, commonData): time = x maxSpeed = commonData['maxSpeed'] mass = commonData['mass'] @@ -43,13 +43,13 @@ class Time2SpeedGetter(SmoothPointGetter): class Time2DistanceGetter(SmoothPointGetter): - def _getCommonData(self, miscParams, fit, tgt): + def _getCommonData(self, miscParams, src, tgt): return { - 'maxSpeed': fit.ship.getModifiedItemAttr('maxVelocity'), - 'mass': fit.ship.getModifiedItemAttr('mass'), - 'agility': fit.ship.getModifiedItemAttr('agility')} + 'maxSpeed': src.getMaxVelocity(), + 'mass': src.item.ship.getModifiedItemAttr('mass'), + 'agility': src.item.ship.getModifiedItemAttr('agility')} - def _calculatePoint(self, x, miscParams, fit, tgt, commonData): + def _calculatePoint(self, x, miscParams, src, tgt, commonData): time = x maxSpeed = commonData['maxSpeed'] mass = commonData['mass'] diff --git a/graphs/data/fitMobility/graph.py b/graphs/data/fitMobility/graph.py index 2598d20a1..9d243cddd 100644 --- a/graphs/data/fitMobility/graph.py +++ b/graphs/data/fitMobility/graph.py @@ -41,4 +41,4 @@ class FitMobilityVsTimeGraph(FitGraph): ('time', 'speed'): Time2SpeedGetter, ('time', 'distance'): Time2DistanceGetter} _denormalizers = { - ('distance', 'km'): lambda v, fit, tgt: v / 1000} + ('distance', 'km'): lambda v, src, tgt: v / 1000} diff --git a/graphs/data/fitShieldRegen/getter.py b/graphs/data/fitShieldRegen/getter.py index 5135ef4e9..484b03c29 100644 --- a/graphs/data/fitShieldRegen/getter.py +++ b/graphs/data/fitShieldRegen/getter.py @@ -25,12 +25,12 @@ from graphs.data.base import SmoothPointGetter class Time2ShieldAmountGetter(SmoothPointGetter): - def _getCommonData(self, miscParams, fit, tgt): + def _getCommonData(self, miscParams, src, tgt): return { - 'maxShieldAmount': fit.ship.getModifiedItemAttr('shieldCapacity'), - 'shieldRegenTime': fit.ship.getModifiedItemAttr('shieldRechargeRate') / 1000} + 'maxShieldAmount': src.item.ship.getModifiedItemAttr('shieldCapacity'), + 'shieldRegenTime': src.item.ship.getModifiedItemAttr('shieldRechargeRate') / 1000} - def _calculatePoint(self, x, miscParams, fit, tgt, commonData): + def _calculatePoint(self, x, miscParams, src, tgt, commonData): time = x shieldAmount = calculateShieldAmount( maxShieldAmount=commonData['maxShieldAmount'], @@ -41,12 +41,12 @@ class Time2ShieldAmountGetter(SmoothPointGetter): class Time2ShieldRegenGetter(SmoothPointGetter): - def _getCommonData(self, miscParams, fit, tgt): + def _getCommonData(self, miscParams, src, tgt): return { - 'maxShieldAmount': fit.ship.getModifiedItemAttr('shieldCapacity'), - 'shieldRegenTime': fit.ship.getModifiedItemAttr('shieldRechargeRate') / 1000} + 'maxShieldAmount': src.item.ship.getModifiedItemAttr('shieldCapacity'), + 'shieldRegenTime': src.item.ship.getModifiedItemAttr('shieldRechargeRate') / 1000} - def _calculatePoint(self, x, miscParams, fit, tgt, commonData): + def _calculatePoint(self, x, miscParams, src, tgt, commonData): time = x shieldAmount = calculateShieldAmount( maxShieldAmount=commonData['maxShieldAmount'], @@ -62,19 +62,19 @@ class Time2ShieldRegenGetter(SmoothPointGetter): # Useless, but valid combination of x and y class ShieldAmount2ShieldAmountGetter(SmoothPointGetter): - def _calculatePoint(self, x, miscParams, fit, tgt, commonData): + def _calculatePoint(self, x, miscParams, src, tgt, commonData): shieldAmount = x return shieldAmount class ShieldAmount2ShieldRegenGetter(SmoothPointGetter): - def _getCommonData(self, miscParams, fit, tgt): + def _getCommonData(self, miscParams, src, tgt): return { - 'maxShieldAmount': fit.ship.getModifiedItemAttr('shieldCapacity'), - 'shieldRegenTime': fit.ship.getModifiedItemAttr('shieldRechargeRate') / 1000} + 'maxShieldAmount': src.item.ship.getModifiedItemAttr('shieldCapacity'), + 'shieldRegenTime': src.item.ship.getModifiedItemAttr('shieldRechargeRate') / 1000} - def _calculatePoint(self, x, miscParams, fit, tgt, commonData): + def _calculatePoint(self, x, miscParams, src, tgt, commonData): shieldAmount = x shieldRegen = calculateShieldRegen( maxShieldAmount=commonData['maxShieldAmount'], diff --git a/graphs/data/fitShieldRegen/graph.py b/graphs/data/fitShieldRegen/graph.py index c05e297c3..498135105 100644 --- a/graphs/data/fitShieldRegen/graph.py +++ b/graphs/data/fitShieldRegen/graph.py @@ -46,15 +46,15 @@ class FitShieldRegenGraph(FitGraph): # Calculation stuff _normalizers = { - ('shieldAmount', '%'): lambda v, fit, tgt: v / 100 * fit.ship.getModifiedItemAttr('shieldCapacity')} + ('shieldAmount', '%'): lambda v, src, tgt: v / 100 * src.item.ship.getModifiedItemAttr('shieldCapacity')} _limiters = { - 'shieldAmount': lambda fit, tgt: (0, fit.ship.getModifiedItemAttr('shieldCapacity'))} + 'shieldAmount': lambda src, tgt: (0, src.item.ship.getModifiedItemAttr('shieldCapacity'))} _getters = { ('time', 'shieldAmount'): Time2ShieldAmountGetter, ('time', 'shieldRegen'): Time2ShieldRegenGetter, ('shieldAmount', 'shieldAmount'): ShieldAmount2ShieldAmountGetter, ('shieldAmount', 'shieldRegen'): ShieldAmount2ShieldRegenGetter} _denormalizers = { - ('shieldAmount', '%'): lambda v, fit, tgt: v * 100 / fit.ship.getModifiedItemAttr('shieldCapacity'), - ('shieldAmount', 'EHP'): lambda v, fit, tgt: fit.damagePattern.effectivify(fit, v, 'shield'), - ('shieldRegen', 'EHP/s'): lambda v, fit, tgt: fit.damagePattern.effectivify(fit, v, 'shield')} + ('shieldAmount', '%'): lambda v, src, tgt: v * 100 / src.item.ship.getModifiedItemAttr('shieldCapacity'), + ('shieldAmount', 'EHP'): lambda v, src, tgt: src.item.damagePattern.effectivify(src.item, v, 'shield'), + ('shieldRegen', 'EHP/s'): lambda v, src, tgt: src.item.damagePattern.effectivify(src.item, v, 'shield')} diff --git a/graphs/data/fitWarpTime/cache.py b/graphs/data/fitWarpTime/cache.py index abbd62d2b..d8d1eda52 100644 --- a/graphs/data/fitWarpTime/cache.py +++ b/graphs/data/fitWarpTime/cache.py @@ -24,9 +24,9 @@ from graphs.data.base import FitDataCache class SubwarpSpeedCache(FitDataCache): - def getSubwarpSpeed(self, fit): + def getSubwarpSpeed(self, src): try: - subwarpSpeed = self._data[fit.ID] + subwarpSpeed = self._data[src.item.ID] except KeyError: modStates = {} disallowedGroups = ( @@ -40,34 +40,34 @@ class SubwarpSpeedCache(FitDataCache): 'Cynosural Field Generator', 'Clone Vat Bay', 'Jump Portal Generator') - for mod in fit.modules: + for mod in src.item.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) + for projFit in src.item.projectedFits: + projectionInfo = projFit.getProjectionInfo(src.item.ID) if projectionInfo is not None and projectionInfo.active: projFitStates[projectionInfo] = projectionInfo.active projectionInfo.active = False projModStates = {} - for mod in fit.projectedModules: + for mod in src.item.projectedModules: if not mod.isExclusiveSystemEffect and mod.state >= FittingModuleState.ACTIVE: projModStates[mod] = mod.state mod.state = FittingModuleState.ONLINE projDroneStates = {} - for drone in fit.projectedDrones: + for drone in src.item.projectedDrones: if drone.amountActive > 0: projDroneStates[drone] = drone.amountActive drone.amountActive = 0 projFighterStates = {} - for fighter in fit.projectedFighters: + for fighter in src.item.projectedFighters: if fighter.active: projFighterStates[fighter] = fighter.active fighter.active = False - fit.calculateModifiedAttributes() - subwarpSpeed = fit.ship.getModifiedItemAttr('maxVelocity') - self._data[fit.ID] = subwarpSpeed + src.item.calculateModifiedAttributes() + subwarpSpeed = src.getMaxVelocity() + self._data[src.item.ID] = subwarpSpeed for projInfo, state in projFitStates.items(): projInfo.active = state for mod, state in modStates.items(): @@ -78,5 +78,5 @@ class SubwarpSpeedCache(FitDataCache): drone.amountActive = amountActive for fighter, state in projFighterStates.items(): fighter.active = state - fit.calculateModifiedAttributes() + src.item.calculateModifiedAttributes() return subwarpSpeed diff --git a/graphs/data/fitWarpTime/getter.py b/graphs/data/fitWarpTime/getter.py index 5245ea0e9..554813b28 100644 --- a/graphs/data/fitWarpTime/getter.py +++ b/graphs/data/fitWarpTime/getter.py @@ -30,12 +30,12 @@ class Distance2TimeGetter(SmoothPointGetter): _baseResolution = 500 - def _getCommonData(self, miscParams, fit, tgt): + def _getCommonData(self, miscParams, src, tgt): return { - 'subwarpSpeed': self.graph._subspeedCache.getSubwarpSpeed(fit), - 'warpSpeed': fit.warpSpeed} + 'subwarpSpeed': self.graph._subspeedCache.getSubwarpSpeed(src), + 'warpSpeed': src.item.warpSpeed} - def _calculatePoint(self, x, miscParams, fit, tgt, commonData): + def _calculatePoint(self, x, miscParams, src, tgt, commonData): distance = x time = calculate_time_in_warp( max_subwarp_speed=commonData['subwarpSpeed'], diff --git a/graphs/data/fitWarpTime/graph.py b/graphs/data/fitWarpTime/graph.py index b2d02fdcd..169e07102 100644 --- a/graphs/data/fitWarpTime/graph.py +++ b/graphs/data/fitWarpTime/graph.py @@ -51,12 +51,12 @@ class FitWarpTimeGraph(FitGraph): # Calculation stuff _normalizers = { - ('distance', 'AU'): lambda v, fit, tgt: v * AU_METERS, - ('distance', 'km'): lambda v, fit, tgt: v * 1000} + ('distance', 'AU'): lambda v, src, tgt: v * AU_METERS, + ('distance', 'km'): lambda v, src, tgt: v * 1000} _limiters = { - 'distance': lambda fit, tgt: (0, fit.maxWarpDistance * AU_METERS)} + 'distance': lambda src, tgt: (0, src.item.maxWarpDistance * AU_METERS)} _getters = { ('distance', 'time'): Distance2TimeGetter} _denormalizers = { - ('distance', 'AU'): lambda v, fit, tgt: v / AU_METERS, - ('distance', 'km'): lambda v, fit, tgt: v / 1000} + ('distance', 'AU'): lambda v, src, tgt: v / AU_METERS, + ('distance', 'km'): lambda v, src, tgt: v / 1000} diff --git a/graphs/gui/frame.py b/graphs/gui/frame.py index 0c3a92b14..3ef624139 100644 --- a/graphs/gui/frame.py +++ b/graphs/gui/frame.py @@ -247,15 +247,20 @@ class GraphFrame(wx.Frame): mainInput, miscInputs = self.ctrlPanel.getValues() view = self.getView() - fits = self.ctrlPanel.fits + sources = self.ctrlPanel.sources if view.hasTargets: - targets = self.ctrlPanel.targets - iterList = tuple(itertools.product(fits, targets)) + iterList = tuple(itertools.product(sources, self.ctrlPanel.targets)) else: - iterList = tuple((f, None) for f in fits) - for fit, target in iterList: + iterList = tuple((f, None) for f in sources) + for source, target in iterList: try: - xs, ys = view.getPlotPoints(mainInput, miscInputs, chosenX, chosenY, fit, target) + xs, ys = view.getPlotPoints( + mainInput=mainInput, + miscInputs=miscInputs, + xSpec=chosenX, + ySpec=chosenY, + src=source, + tgt=target) # Figure out min and max Y min_y_this = min(ys, default=None) @@ -275,11 +280,11 @@ class GraphFrame(wx.Frame): self.subplot.plot(xs, ys) if target is None: - legend.append(self.getObjName(fit)) + legend.append(source.shortName) else: - legend.append('{} vs {}'.format(self.getObjName(fit), self.getObjName(target))) + legend.append('{} vs {}'.format(source.shortName, target.shortName)) except Exception as ex: - pyfalog.warning('Invalid values in "{0}"', fit.name) + pyfalog.warning('Invalid values in "{0}"', source.name) self.canvas.draw() self.Refresh() return @@ -330,12 +335,3 @@ class GraphFrame(wx.Frame): self.canvas.draw() self.Refresh() - - @staticmethod - def getObjName(thing): - if isinstance(thing, Fit): - return '{} ({})'.format(thing.name, thing.ship.item.getShortName()) - elif isinstance(thing, TargetProfile): - return thing.name - return '' - diff --git a/graphs/gui/lists.py b/graphs/gui/lists.py index 43e24dd33..512df8193 100644 --- a/graphs/gui/lists.py +++ b/graphs/gui/lists.py @@ -23,12 +23,13 @@ import wx import gui.display from eos.saveddata.targetProfile import TargetProfile +from graphs.wrapper import SourceWrapper, TargetWrapper from gui.contextMenu import ContextMenu from service.const import GraphCacheCleanupReason from service.fit import Fit -class BaseList(gui.display.Display): +class BaseWrapperList(gui.display.Display): DEFAULT_COLS = ( 'Base Icon', @@ -37,7 +38,7 @@ class BaseList(gui.display.Display): def __init__(self, graphFrame, parent): super().__init__(parent) self.graphFrame = graphFrame - self.fits = [] + self._wrappers = [] self.hoveredRow = None self.hoveredColumn = None @@ -47,6 +48,15 @@ class BaseList(gui.display.Display): self.Bind(wx.EVT_MOTION, self.OnMouseMove) self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow) + @property + def wrappers(self): + return sorted(self._wrappers, key=lambda w: w.isFit) + + # UI-related stuff + @property + def defaultTTText(self): + raise NotImplementedError + def refreshExtraColumns(self, extraColSpecs): baseColNames = set() for baseColName in self.DEFAULT_COLS: @@ -63,31 +73,13 @@ class BaseList(gui.display.Display): self.appendColumnBySpec(colSpec) self.refreshView() - def handleDrag(self, type, fitID): - if type == 'fit': - sFit = Fit.getInstance() - fit = sFit.getFit(fitID) - if fit not in self.fits: - self.fits.append(fit) - self.updateView() - self.graphFrame.draw() + def refreshView(self): + self.refresh(self.wrappers) - def kbEvent(self, event): - keycode = event.GetKeyCode() - mstate = wx.GetMouseState() - if keycode == 65 and mstate.GetModifiers() == wx.MOD_CONTROL: - self.selectAll() - elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE) and mstate.GetModifiers() == wx.MOD_NONE: - self.removeListItems(self.getSelectedListItems()) - event.Skip() - - def OnLeftDClick(self, event): - row, _ = self.HitTest(event.Position) - item = self.getListItem(row) - if item is None: - return - self.removeListItems([item]) + def updateView(self): + self.update(self.wrappers) + # UI event handling def OnMouseMove(self, event): row, _, col = self.HitTestSubItem(event.Position) if row != self.hoveredRow or col != self.hoveredColumn: @@ -97,7 +89,7 @@ class BaseList(gui.display.Display): self.hoveredRow = row self.hoveredColumn = col if row != -1 and col != -1 and col < self.ColumnCount: - item = self.getListItem(row) + item = self.getWrapper(row) if item is None: return tooltip = self.activeColumns[col].getToolTip(item) @@ -115,70 +107,138 @@ class BaseList(gui.display.Display): self.hoveredColumn = None event.Skip() - # Fit events + def handleDrag(self, type, fitID): + if type == 'fit' and not self.containsFitID(fitID): + sFit = Fit.getInstance() + fit = sFit.getFit(fitID) + self.appendItem(fit) + self.updateView() + self.graphFrame.draw() + + def OnLeftDClick(self, event): + row, _ = self.HitTest(event.Position) + wrapper = self.getWrapper(row) + if wrapper is None: + return + self.removeWrappers([wrapper]) + + def kbEvent(self, event): + keycode = event.GetKeyCode() + mstate = wx.GetMouseState() + if keycode == 65 and mstate.GetModifiers() == wx.MOD_CONTROL: + self.selectAll() + elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE) and mstate.GetModifiers() == wx.MOD_NONE: + self.removeWrappers(self.getSelectedWrappers()) + event.Skip() + + # Wrapper-related methods + @property + def wrapperClass(self): + raise NotImplementedError + + def getWrapper(self, row): + if row == -1: + return None + try: + return self._wrappers[row] + except IndexError: + return None + + def removeWrappers(self, wrappers): + wrappers = set(wrappers).union(self._wrappers) + if not wrappers: + return + for wrapper in wrappers: + self._wrappers.remove(wrapper) + self.updateView() + for wrapper in wrappers: + if wrapper.isFit: + self.graphFrame.clearCache(reason=GraphCacheCleanupReason.fitRemoved, extraData=wrapper.fitID) + elif wrapper.isProfile: + self.graphFrame.clearCache(reason=GraphCacheCleanupReason.profileRemoved, extraData=wrapper.profileID) + self.graphFrame.draw() + + def getSelectedWrappers(self): + wrappers = [] + for row in self.getSelectedRows(): + wrapper = self.getWrapper(row) + if wrapper is None: + continue + wrappers.append(wrapper) + return wrappers + + def appendItem(self, item): + self._wrappers.append(self.wrapperClass(item)) + + def containsFitID(self, fitID): + for wrapper in self._wrappers: + if wrapper.isFit and wrapper.itemID == fitID: + return True + return False + + def containsProfileID(self, profileID): + for wrapper in self._wrappers: + if wrapper.isProfile and wrapper.itemID == profileID: + return True + return False + + # Wrapper-related events def OnFitRenamed(self, event): - if event.fitID in [f.ID for f in self.fits]: + if self.containsFitID(event.fitID): self.updateView() def OnFitChanged(self, event): - if set(event.fitIDs).union(f.ID for f in self.fits): + if set(event.fitIDs).union(w.itemID for w in self._wrappers if w.isFit): self.updateView() def OnFitRemoved(self, event): - fit = next((f for f in self.fits if f.ID == event.fitID), None) - if fit is not None: - self.fits.remove(fit) + wrapper = next((w for w in self._wrappers if w.isFit and w.itemID == event.fitID), None) + if wrapper is not None: + self._wrappers.remove(wrapper) self.updateView() - @property - def defaultTTText(self): - raise NotImplementedError + def OnProfileRenamed(self, event): + if self.containsProfileID(event.profileID): + self.updateView() - def refreshView(self): - raise NotImplementedError + def OnProfileChanged(self, event): + if self.containsProfileID(event.profileID): + self.updateView() - def updateView(self): - raise NotImplementedError - - def getListItem(self, row): - raise NotImplementedError - - def removeListItems(self, items): - raise NotImplementedError - - def getSelectedListItems(self): - items = [] - for row in self.getSelectedRows(): - item = self.getListItem(row) - if item is None: - continue - items.append(item) - return items + def OnProfileRemoved(self, event): + wrapper = next((w for w in self._wrappers if w.isProfile and w.itemID == event.profileID), None) + if wrapper is not None: + self._wrappers.remove(wrapper) + self.updateView() # Context menu handlers def addFit(self, fit): if fit is None: return - if fit in self.fits: + if self.containsFitID(fit.ID): return - self.fits.append(fit) + self.appendItem(fit) self.updateView() self.graphFrame.draw() def getExistingFitIDs(self): - return [f.ID for f in self.fits] + return [w.itemID for w in self._wrappers if w.isFit] def addFitsByIDs(self, fitIDs): sFit = Fit.getInstance() for fitID in fitIDs: + if self.containsFitID(fitID): + continue fit = sFit.getFit(fitID) if fit is not None: - self.fits.append(fit) + self.appendItem(fit) self.updateView() self.graphFrame.draw() -class FitList(BaseList): +class SourceWrapperList(BaseWrapperList): + + wrapperClass = SourceWrapper def __init__(self, graphFrame, parent): super().__init__(graphFrame, parent) @@ -187,19 +247,13 @@ class FitList(BaseList): fit = Fit.getInstance().getFit(self.graphFrame.mainFrame.getActiveFit()) if fit is not None: - self.fits.append(fit) + self.appendItem(fit) self.updateView() - def refreshView(self): - self.refresh(self.fits) - - def updateView(self): - self.update(self.fits) - def spawnMenu(self, event): - selection = self.getSelectedListItems() + selection = self.getSelectedWrappers() clickedPos = self.getRowByAbs(event.Position) - mainItem = self.getListItem(clickedPos) + mainItem = self.getWrapper(clickedPos) sourceContext = 'graphFitList' itemContext = None if mainItem is None else 'Fit' @@ -207,51 +261,27 @@ class FitList(BaseList): if menu: self.PopupMenu(menu) - def getListItem(self, row): - if row == -1: - return None - try: - return self.fits[row] - except IndexError: - return None - - def removeListItems(self, items): - toRemove = [i for i in items if i in self.fits] - if not toRemove: - return - for fit in toRemove: - self.fits.remove(fit) - self.updateView() - for fit in toRemove: - self.graphFrame.clearCache(reason=GraphCacheCleanupReason.fitRemoved, extraData=fit.ID) - self.graphFrame.draw() - @property def defaultTTText(self): return 'Drag a fit into this list to graph it' -class TargetList(BaseList): +class TargetWrapperList(BaseWrapperList): + + wrapperClass = TargetWrapper def __init__(self, graphFrame, parent): super().__init__(graphFrame, parent) self.Bind(wx.EVT_CONTEXT_MENU, self.spawnMenu) - self.profiles = [] - self.profiles.append(TargetProfile.getIdeal()) + self.appendItem(TargetProfile.getIdeal()) self.updateView() - def refreshView(self): - self.refresh(self.targets) - - def updateView(self): - self.update(self.targets) - def spawnMenu(self, event): - selection = self.getSelectedListItems() + selection = self.getSelectedWrappers() clickedPos = self.getRowByAbs(event.Position) - mainItem = self.getListItem(clickedPos) + mainItem = self.getWrapper(clickedPos) sourceContext = 'graphTgtList' itemContext = None if mainItem is None else 'Target' @@ -259,56 +289,6 @@ class TargetList(BaseList): if menu: self.PopupMenu(menu) - def getListItem(self, row): - if row == -1: - return None - - numFits = len(self.fits) - numProfiles = len(self.profiles) - - if (numFits + numProfiles) == 0: - return None - - if row < numFits: - return self.fits[row] - else: - return self.profiles[row - numFits] - - def removeListItems(self, items): - fitsToRemove = [i for i in items if i in self.fits] - profilesToRemove = [i for i in items if i in self.profiles] - if not fitsToRemove and not profilesToRemove: - return - for fit in fitsToRemove: - self.fits.remove(fit) - for profile in profilesToRemove: - self.profiles.remove(profile) - self.updateView() - for fit in fitsToRemove: - self.graphFrame.clearCache(reason=GraphCacheCleanupReason.fitRemoved, extraData=fit.ID) - for profile in profilesToRemove: - self.graphFrame.clearCache(reason=GraphCacheCleanupReason.profileRemoved, extraData=profile.ID) - self.graphFrame.draw() - - # Target profile events - def OnProfileRenamed(self, event): - if event.profileID in [tp.ID for tp in self.profiles]: - self.updateView() - - def OnProfileChanged(self, event): - if event.profileID in [tp.ID for tp in self.profiles]: - self.updateView() - - def OnProfileRemoved(self, event): - profile = next((tp for tp in self.profiles if tp.ID == event.profileID), None) - if profile is not None: - self.profiles.remove(profile) - self.updateView() - - @property - def targets(self): - return self.fits + self.profiles - @property def defaultTTText(self): return 'Drag a fit into this list to have your fits graphed against it' @@ -317,8 +297,8 @@ class TargetList(BaseList): def addProfile(self, profile): if profile is None: return - if profile in self.profiles: + if self.containsProfileID(profile.ID): return - self.profiles.append(profile) + self.appendItem(profile) self.updateView() self.graphFrame.draw() diff --git a/graphs/gui/panel.py b/graphs/gui/panel.py index 8db9c3cb2..484d608b6 100644 --- a/graphs/gui/panel.py +++ b/graphs/gui/panel.py @@ -28,7 +28,7 @@ from gui.contextMenu import ContextMenu from gui.utils.inputs import FloatBox, FloatRangeBox from service.const import GraphCacheCleanupReason from service.fit import Fit -from .lists import FitList, TargetList +from .lists import SourceWrapperList, TargetWrapperList from .vector import VectorPicker @@ -114,10 +114,10 @@ class GraphControlPanel(wx.Panel): mainSizer.Add(optsSizer, 0, wx.EXPAND | wx.ALL, 10) srcTgtSizer = wx.BoxSizer(wx.HORIZONTAL) - self.fitList = FitList(graphFrame, self) - self.fitList.SetMinSize((270, -1)) - srcTgtSizer.Add(self.fitList, 1, wx.EXPAND | wx.ALL, 0) - self.targetList = TargetList(graphFrame, self) + self.sourceList = SourceWrapperList(graphFrame, self) + self.sourceList.SetMinSize((270, -1)) + srcTgtSizer.Add(self.sourceList, 1, wx.EXPAND | wx.ALL, 0) + self.targetList = TargetWrapperList(graphFrame, self) self.targetList.SetMinSize((270, -1)) srcTgtSizer.Add(self.targetList, 1, wx.EXPAND | wx.LEFT, 10) mainSizer.Add(srcTgtSizer, 1, wx.EXPAND | wx.LEFT | wx.BOTTOM | wx.RIGHT, 10) @@ -162,7 +162,7 @@ class GraphControlPanel(wx.Panel): self.tgtVectorLabel.Show(False) # Source and target list - self.fitList.refreshExtraColumns(view.srcExtraCols) + self.sourceList.refreshExtraColumns(view.srcExtraCols) self.targetList.refreshExtraColumns(view.tgtExtraCols) self.targetList.Show(view.hasTargets) @@ -333,34 +333,37 @@ class GraphControlPanel(wx.Panel): return self.xSubSelection.GetClientData(self.xSubSelection.GetSelection()) @property - def fits(self): - return self.fitList.fits + def sources(self): + return self.sourceList.wrappers @property def targets(self): - return self.targetList.targets + return self.targetList.wrappers # Fit events def OnFitRenamed(self, event): - self.fitList.OnFitRenamed(event) + self.sourceList.OnFitRenamed(event) self.targetList.OnFitRenamed(event) def OnFitChanged(self, event): - self.fitList.OnFitChanged(event) + self.sourceList.OnFitChanged(event) self.targetList.OnFitChanged(event) def OnFitRemoved(self, event): - self.fitList.OnFitRemoved(event) + self.sourceList.OnFitRemoved(event) self.targetList.OnFitRemoved(event) # Target profile events def OnProfileRenamed(self, event): + self.sourceList.OnProfileRenamed(event) self.targetList.OnProfileRenamed(event) def OnProfileChanged(self, event): + self.sourceList.OnProfileChanged(event) self.targetList.OnProfileChanged(event) def OnProfileRemoved(self, event): + self.sourceList.OnProfileRemoved(event) self.targetList.OnProfileRemoved(event) def formatLabel(self, axisDef): diff --git a/graphs/wrapper.py b/graphs/wrapper.py new file mode 100644 index 000000000..6d485ae30 --- /dev/null +++ b/graphs/wrapper.py @@ -0,0 +1,131 @@ +# ============================================================================= +# 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 . +# ============================================================================= + + +import math + +from eos.saveddata.fit import Fit +from eos.saveddata.targetProfile import TargetProfile + + +class BaseWrapper: + + def __init__(self, item): + self.item = item + + @property + def isFit(self): + return isinstance(self.item, Fit) + + @property + def isProfile(self): + return isinstance(self.item, TargetProfile) + + @property + def itemID(self): + return self.item.ID + + @property + def name(self): + if self.isFit: + return '{} ({})'.format(self.item.name, self.item.ship.item.name) + elif self.isProfile: + return self.item.name + return '' + + @property + def shortName(self): + if self.isFit: + return '{} ({})'.format(self.item.name, self.item.ship.item.getShortName()) + elif self.isProfile: + return self.item.name + return '' + + def getMaxVelocity(self, extraMultipliers=None): + if self.isFit: + if extraMultipliers: + maxVelocity = self.item.ship.getModifiedItemAttrWithExtraMods('maxVelocity', extraMultipliers=extraMultipliers) + else: + maxVelocity = self.item.ship.getModifiedItemAttr('maxVelocity') + elif self.isProfile: + maxVelocity = self.item.maxVelocity + if extraMultipliers: + maxVelocity *= _calculateMultiplier(extraMultipliers) + else: + maxVelocity = None + return maxVelocity + + def getSigRadius(self, extraMultipliers=None): + if self.isFit: + if extraMultipliers: + sigRadius = self.item.ship.getModifiedItemAttrWithExtraMods('signatureRadius', extraMultipliers=extraMultipliers) + else: + sigRadius = self.item.ship.getModifiedItemAttr('signatureRadius') + elif self.isProfile: + sigRadius = self.item.signatureRadius + if extraMultipliers: + sigRadius *= _calculateMultiplier(extraMultipliers) + else: + sigRadius = None + return sigRadius + + def getRadius(self): + if self.isFit: + radius = self.item.ship.getModifiedItemAttr('radius') + elif self.isProfile: + radius = self.item.radius + else: + radius = None + return radius + + +class SourceWrapper(BaseWrapper): + pass + + +class TargetWrapper(BaseWrapper): + + def __init__(self, item): + super().__init__(item=item) + self.resistMode = None + + +# Just copy-paste penalization chain calculation code (with some modifications, +# as multipliers arrive in different form) in here to not make actual attribute +# calculations slower than they already are due to extra function calls +def _calculateMultiplier(multipliers): + val = 1 + for penalizedMultipliers in multipliers.values(): + # A quick explanation of how this works: + # 1: Bonuses and penalties are calculated seperately, so we'll have to filter each of them + l1 = [v[0] for v in penalizedMultipliers if v[0] > 1] + l2 = [v[0] for v in penalizedMultipliers if v[0] < 1] + # 2: The most significant bonuses take the smallest penalty, + # This means we'll have to sort + abssort = lambda _val: -abs(_val - 1) + l1.sort(key=abssort) + l2.sort(key=abssort) + # 3: The first module doesn't get penalized at all + # Any module after the first takes penalties according to: + # 1 + (multiplier - 1) * math.exp(- math.pow(i, 2) / 7.1289) + for l in (l1, l2): + for i in range(len(l)): + bonus = l[i] + val *= 1 + (bonus - 1) * math.exp(- i ** 2 / 7.1289) + return val diff --git a/gui/builtinContextMenus/itemRemove.py b/gui/builtinContextMenus/itemRemove.py index 381516435..2417ad302 100644 --- a/gui/builtinContextMenus/itemRemove.py +++ b/gui/builtinContextMenus/itemRemove.py @@ -161,7 +161,7 @@ class RemoveItem(ContextMenuCombined): fitID=fitID, commandFitIDs=commandFitIDs)) def __handleGraphItem(self, callingWindow, mainItem, selection): - callingWindow.removeListItems(selection) + callingWindow.removeWrappers(selection) RemoveItem.register() diff --git a/gui/builtinViewColumns/attributeDisplayGraph.py b/gui/builtinViewColumns/attributeDisplayGraph.py index 76352217f..b1f2bedf8 100644 --- a/gui/builtinViewColumns/attributeDisplayGraph.py +++ b/gui/builtinViewColumns/attributeDisplayGraph.py @@ -27,9 +27,10 @@ import gui.mainFrame from eos.saveddata.fit import Fit from eos.saveddata.targetProfile import TargetProfile from eos.utils.spoolSupport import SpoolOptions, SpoolType +from graphs.wrapper import BaseWrapper from gui.bitmap_loader import BitmapLoader -from gui.viewColumn import ViewColumn from gui.utils.numberFormatter import formatAmount +from gui.viewColumn import ViewColumn class GraphColumn(ViewColumn, metaclass=ABCMeta): @@ -47,6 +48,8 @@ class GraphColumn(ViewColumn, metaclass=ABCMeta): raise NotImplementedError def getText(self, stuff): + if isinstance(stuff, BaseWrapper): + stuff = stuff.item if isinstance(stuff, (Fit, TargetProfile)): val, unit = self._getValue(stuff) if val is None: @@ -59,6 +62,8 @@ class GraphColumn(ViewColumn, metaclass=ABCMeta): raise NotImplementedError def getToolTip(self, stuff): + if isinstance(stuff, BaseWrapper): + stuff = stuff.item if isinstance(stuff, (Fit, TargetProfile)): return self._getFitTooltip() return '' diff --git a/gui/builtinViewColumns/baseIcon.py b/gui/builtinViewColumns/baseIcon.py index 11d79689b..1afac7fbb 100644 --- a/gui/builtinViewColumns/baseIcon.py +++ b/gui/builtinViewColumns/baseIcon.py @@ -1,11 +1,13 @@ # noinspection PyPackageRequirements import wx -from eos.saveddata.implant import Implant -from eos.saveddata.drone import Drone -from eos.saveddata.module import Module, Rack -from eos.saveddata.fit import Fit -from eos.saveddata.targetProfile import TargetProfile + from eos.const import FittingSlot +from eos.saveddata.drone import Drone +from eos.saveddata.fit import Fit +from eos.saveddata.implant import Implant +from eos.saveddata.module import Module, Rack +from eos.saveddata.targetProfile import TargetProfile +from graphs.wrapper import BaseWrapper from gui.viewColumn import ViewColumn @@ -21,6 +23,9 @@ class BaseIcon(ViewColumn): self.shipImage = fittingView.imageList.GetImageIndex("ship_small", "gui") def getImageId(self, stuff): + if isinstance(stuff, BaseWrapper): + stuff = stuff.item + if isinstance(stuff, Drone): return -1 elif isinstance(stuff, Fit): diff --git a/gui/builtinViewColumns/baseName.py b/gui/builtinViewColumns/baseName.py index e5fb772c6..57c3ba99f 100644 --- a/gui/builtinViewColumns/baseName.py +++ b/gui/builtinViewColumns/baseName.py @@ -21,19 +21,22 @@ # noinspection PyPackageRequirements import wx from logbook import Logger + +import gui.mainFrame +from eos.const import FittingSlot from eos.saveddata.cargo import Cargo -from eos.saveddata.implant import Implant from eos.saveddata.drone import Drone from eos.saveddata.fighter import Fighter -from eos.saveddata.module import Module, Rack from eos.saveddata.fit import Fit, FitLite +from eos.saveddata.implant import Implant +from eos.saveddata.module import Module, Rack from eos.saveddata.targetProfile import TargetProfile -from eos.const import FittingSlot +from graphs.wrapper import BaseWrapper +from gui.builtinContextMenus.envEffectAdd import AddEnvironmentEffect +from gui.viewColumn import ViewColumn from service.fit import Fit as FitSvc from service.market import Market -from gui.viewColumn import ViewColumn -from gui.builtinContextMenus.envEffectAdd import AddEnvironmentEffect -import gui.mainFrame + pyfalog = Logger(__name__) @@ -50,6 +53,9 @@ class BaseName(ViewColumn): self.projectedView = isinstance(fittingView, gui.builtinAdditionPanes.projectedView.ProjectedView) def getText(self, stuff): + if isinstance(stuff, BaseWrapper): + stuff = stuff.item + if isinstance(stuff, Drone): return "%dx %s" % (stuff.amount, stuff.item.name) elif isinstance(stuff, Fighter):