diff --git a/eos/graph/fitDmgVsTime.py b/eos/graph/fitDmgVsTime.py index 1d8aba564..8b29de539 100644 --- a/eos/graph/fitDmgVsTime.py +++ b/eos/graph/fitDmgVsTime.py @@ -31,6 +31,8 @@ class FitDmgVsTimeGraph(Graph): if fit.ID not in self.cache: self.__generateCache(fit, maxTime) currentY = 0 + # Add zeros even if there's some damage dealt at time = 0, to explicitly show that + # volley is done at this time xs = [0] ys = [0] cache = self.cache[fit.ID] diff --git a/eos/graph/fitDpsTime.py b/eos/graph/fitDpsVsTime.py similarity index 58% rename from eos/graph/fitDpsTime.py rename to eos/graph/fitDpsVsTime.py index 34bad137c..c5399db37 100644 --- a/eos/graph/fitDpsTime.py +++ b/eos/graph/fitDpsVsTime.py @@ -17,42 +17,81 @@ # along with eos. If not, see . # =============================================================================== -from logbook import Logger + +from itertools import chain from eos.graph import Graph from eos.utils.spoolSupport import SpoolType, SpoolOptions -pyfalog = Logger(__name__) - - class FitDpsTimeGraph(Graph): - defaults = {"time": 0} + def getPlotPoints(self, fit, extraData, xRange, xAmount): + # We deliberately ignore xAmount here to build graph which will reflect + # all steps of building up the damage + minX, maxX = xRange + if fit.ID not in self.cache: + self.__generateCache(fit, maxX) + currentY = None + xs = [] + ys = [] + cache = self.cache[fit.ID] + for time in sorted(cache): + prevY = currentY + currentX = time / 1000 + currentY = cache[time] + if currentX < minX: + continue + # First set of data points + if not xs: + # Start at exactly requested time, at last known value + initialY = prevY or 0 + xs.append(minX) + ys.append(initialY) + # If current time is bigger then starting, extend plot to that time with old value + if currentX > minX: + xs.append(currentX) + ys.append(initialY) + # If new value is different, extend it with new point to the new value + if currentY != prevY: + xs.append(currentX) + ys.append(currentY) + continue + # Last data point + if currentX > maxX: + xs.append(maxX) + ys.append(prevY) + break + # Anything in-between + if currentY != prevY: + if prevY is not None: + xs.append(currentX) + ys.append(prevY) + xs.append(currentX) + ys.append(currentY) + if currentX >= maxX: + break + return xs, ys - def __init__(self, fit, data=None): - Graph.__init__(self, fit, self.calcDps, data if data is not None else self.defaults) - self.fit = fit - self.__cache = [] + def getYForX(self, fit, extraData, x): + time = x * 1000 + cache = self.cache[fit.ID] + closestTime = max((t for t in cache if t <= time), default=None) + if closestTime is None: + return 0 + return cache[closestTime] - def calcDps(self, data): - time = data["time"] * 1000 - entries = (e for e in self.__cache if e[0] <= time < e[1]) - dps = sum(e[2] for e in entries) - return dps - - def recalc(self): + def __generateCache(self, fit, maxTime): + cache = [] def addDmg(addedTimeStart, addedTimeFinish, addedDmg): if addedDmg == 0: return addedDps = 1000 * addedDmg / (addedTimeFinish - addedTimeStart) - self.__cache.append((addedTimeStart, addedTimeFinish, addedDps)) + cache.append((addedTimeStart, addedTimeFinish, addedDps)) - self.__cache = [] - fit = self.fit # We'll handle calculations in milliseconds - maxTime = self.data["time"].data[0].end * 1000 + maxTime = maxTime * 1000 for mod in fit.modules: cycleParams = mod.getCycleParameters(reloadOverride=True) if cycleParams is None: @@ -63,8 +102,7 @@ class FitDpsTimeGraph(Graph): cycleDamage = 0 volleyParams = mod.getVolleyParameters(spoolOptions=SpoolOptions(SpoolType.CYCLES, nonstopCycles, True)) for volleyTime, volley in volleyParams.items(): - if currentTime + volleyTime <= maxTime and volleyTime <= cycleTime: - cycleDamage += volley.total + cycleDamage += volley.total addDmg(currentTime, currentTime + cycleTime, cycleDamage) currentTime += cycleTime currentTime += inactiveTime @@ -83,8 +121,7 @@ class FitDpsTimeGraph(Graph): cycleDamage = 0 volleyParams = drone.getVolleyParameters() for volleyTime, volley in volleyParams.items(): - if currentTime + volleyTime <= maxTime and volleyTime <= cycleTime: - cycleDamage += volley.total + cycleDamage += volley.total addDmg(currentTime, currentTime + cycleTime, cycleDamage) currentTime += cycleTime currentTime += inactiveTime @@ -103,10 +140,17 @@ class FitDpsTimeGraph(Graph): for cycleTime, inactiveTime in abilityCycleParams.iterCycles(): cycleDamage = 0 for volleyTime, volley in abilityVolleyParams.items(): - if currentTime + volleyTime <= maxTime and volleyTime <= cycleTime: - cycleDamage += volley.total + cycleDamage += volley.total addDmg(currentTime, currentTime + cycleTime, cycleDamage) currentTime += cycleTime currentTime += inactiveTime if currentTime > maxTime: break + + # Post-process cache + finalCache = {} + for time in sorted(set(chain((i[0] for i in cache), (i[1] for i in cache)))): + entries = (e for e in cache if e[0] <= time < e[1]) + dps = sum(e[2] for e in entries) + finalCache[time] = dps + self.cache[fit.ID] = finalCache diff --git a/gui/builtinGraphs/fitDmgVsTime.py b/gui/builtinGraphs/fitDmgVsTime.py index 21f02e0ef..0b2315ce9 100644 --- a/gui/builtinGraphs/fitDmgVsTime.py +++ b/gui/builtinGraphs/fitDmgVsTime.py @@ -21,6 +21,7 @@ from collections import OrderedDict from eos.graph.fitDmgVsTime import FitDmgVsTimeGraph as EosGraphDmg +from eos.graph.fitDpsVsTime import FitDpsTimeGraph as EosGraphDps from gui.graph import Graph, XDef, YDef @@ -30,6 +31,7 @@ class FitDmgVsTimeGraph(Graph): def __init__(self): self.eosGraphDmg = EosGraphDmg() + self.eosGraphDps = EosGraphDps() @property def xDef(self): @@ -37,7 +39,9 @@ class FitDmgVsTimeGraph(Graph): @property def yDefs(self): - return OrderedDict([('damage', YDef(switchLabel='Damage inflicted', axisLabel='Damage', eosGraph='eosGraphDmg'))]) + return OrderedDict([ + ('dps', YDef(switchLabel='DPS', axisLabel='DPS', eosGraph='eosGraphDps')), + ('damage', YDef(switchLabel='Damage inflicted', axisLabel='Damage', eosGraph='eosGraphDmg'))]) FitDmgVsTimeGraph.register() diff --git a/gui/builtinGraphs/fitDpsTime.py b/gui/builtinGraphs/fitDpsTime.py deleted file mode 100644 index 3629eeda0..000000000 --- a/gui/builtinGraphs/fitDpsTime.py +++ /dev/null @@ -1,83 +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 gui.mainFrame -from eos.graph import Data -from eos.graph.fitDpsTime import FitDpsTimeGraph as EosFitDpsTimeGraph -from gui.bitmap_loader import BitmapLoader -from gui.graph import Graph -from service.attribute import Attribute - - -class FitDpsTimeGraph(Graph): - - propertyLabelMap = {"time": "Time (seconds)"} - - defaults = EosFitDpsTimeGraph.defaults.copy() - - def __init__(self): - Graph.__init__(self) - self.defaults["time"] = "0-80" - self.name = "DPS vs Time" - self.eosGraph = None - self.mainFrame = gui.mainFrame.MainFrame.getInstance() - - def getFields(self): - return self.defaults - - def getLabels(self): - return self.propertyLabelMap - - def getIcons(self): - iconFile = Attribute.getInstance().getAttributeInfo('duration').iconID - bitmap = BitmapLoader.getBitmap(iconFile, "icons") - return {"time": bitmap} - - def getPoints(self, fit, fields): - eosGraph = getattr(self, "eosGraph", None) - if eosGraph is None or eosGraph.fit != fit: - eosGraph = self.eosGraph = EosFitDpsTimeGraph(fit) - - eosGraph.clearData() - variable = None - for fieldName, value in fields.items(): - d = Data(fieldName, value) - if not d.isConstant(): - if variable is None: - variable = fieldName - else: - # We can't handle more then one variable atm, OOPS FUCK OUT - return False, "Can only handle 1 variable" - - eosGraph.setData(d) - - if variable is None: - return False, "No variable" - - x = [] - y = [] - eosGraph.recalc() - for point, val in eosGraph.getIterator(): - x.append(point[variable]) - y.append(val) - - return x, y - - -FitDpsTimeGraph.register()