diff --git a/gui/builtinGraphs/base.py b/gui/builtinGraphs/base.py index f729b21bb..2e6b75018 100644 --- a/gui/builtinGraphs/base.py +++ b/gui/builtinGraphs/base.py @@ -21,6 +21,8 @@ from abc import ABCMeta, abstractmethod from collections import OrderedDict, namedtuple +from service.const import GraphCacheCleanupReason + YDef = namedtuple('YDef', ('handle', 'unit', 'label')) XDef = namedtuple('XDef', ('handle', 'unit', 'label', 'mainInput')) @@ -92,24 +94,26 @@ class FitGraph(metaclass=ABCMeta): self._plotCache.setdefault(cacheKey, {})[(ySpec, xSpec)] = plotData return plotData - def clearCache(self, fitID=None): - # Clear everything - if fitID is None: + def clearCache(self, reason, extraData=None): + # If only one fit changed - clear plots which concern this fit + if reason == GraphCacheCleanupReason.fitChanged: + # Clear plot cache + plotKeysToClear = set() + for cacheKey in self._plotCache: + cacheFitID, cacheTgtType, cacheTgtID = cacheKey + if extraData == cacheFitID: + plotKeysToClear.add(cacheKey) + elif extraData == cacheTgtID: + plotKeysToClear.add(cacheKey) + for cacheKey in plotKeysToClear: + del self._plotCache[cacheKey] + # Wipe out whole plot cache otherwise + else: self._plotCache.clear() - return - # Clear plot cache - plotKeysToClear = set() - for cacheKey in self._plotCache: - cacheFitID, cacheTgtType, cacheTgtID = cacheKey - if fitID == cacheFitID: - plotKeysToClear.add(cacheKey) - elif fitID == cacheTgtID: - plotKeysToClear.add(cacheKey) - for cacheKey in plotKeysToClear: - del self._plotCache[cacheKey] - self._clearInternalCache(fitID=fitID) + # And process any internal caches graphs might have + self._clearInternalCache(reason, extraData) - def _clearInternalCache(self, fitID): + def _clearInternalCache(self, reason, extraData): return # Calculation stuff @@ -219,12 +223,13 @@ class FitDataCache: def __init__(self): self._data = {} - def clear(self, fitID): - if fitID is None: - self._data.clear() - elif fitID in self._data: + def clearForFit(self, fitID): + if fitID in self._data: del self._data[fitID] + def clearAll(self): + self._data.clear() + # noinspection PyUnresolvedReferences from gui.builtinGraphs import * diff --git a/gui/builtinGraphs/fitDamageStats/graph.py b/gui/builtinGraphs/fitDamageStats/graph.py index e407ef724..1d9e96910 100644 --- a/gui/builtinGraphs/fitDamageStats/graph.py +++ b/gui/builtinGraphs/fitDamageStats/graph.py @@ -23,6 +23,7 @@ from eos.const import FittingHardpoint from eos.utils.spoolSupport import SpoolType, SpoolOptions from eos.utils.stats import DmgTypes from gui.builtinGraphs.base import FitGraph, XDef, YDef, Input, VectorDef +from service.const import GraphCacheCleanupReason from .calc import getTurretMult, getLauncherMult, getDroneMult, getFighterAbilityMult, getSmartbombMult, getBombMult, getGuidedBombMult from .timeCache import TimeCache @@ -33,8 +34,16 @@ class FitDamageStatsGraph(FitGraph): super().__init__() self._timeCache = TimeCache() - def _clearInternalCache(self, fitID): - self._timeCache.clear(fitID) + def _clearInternalCache(self, reason, extraData): + # Here, we care only about fit changes and graph changes. + # - Input changes are irrelevant as time cache cares only about + # time input, and it regenerates once time goes beyond cached value + # - Option changes are irrelevant as cache contains "raw" damage + # values which do not rely on any graph options + if reason == GraphCacheCleanupReason.fitChanged: + self._timeCache.clearForFit(extraData) + elif reason == GraphCacheCleanupReason.graphSwitched: + self._timeCache.clearAll() # UI stuff internalName = 'dmgStatsGraph' @@ -283,8 +292,10 @@ class FitDamageStatsGraph(FitGraph): # Damage data per key getters def _getDpsPerKey(self, fit, time): + # Use data from time cache if time was not specified if time is not None: return self._timeCache.getDpsDataPoint(fit, time) + # Compose map ourselves using current fit settings if time is not specified dpsMap = {} defaultSpoolValue = eos.config.settings['globalDefaultSpoolupPercentage'] for mod in fit.modules: @@ -303,8 +314,10 @@ class FitDamageStatsGraph(FitGraph): return dpsMap def _getVolleyPerKey(self, fit, time): + # Use data from time cache if time was not specified if time is not None: return self._timeCache.getVolleyDataPoint(fit, time) + # Compose map ourselves using current fit settings if time is not specified volleyMap = {} defaultSpoolValue = eos.config.settings['globalDefaultSpoolupPercentage'] for mod in fit.modules: diff --git a/gui/builtinGraphs/fitWarpTime.py b/gui/builtinGraphs/fitWarpTime.py index 50d7f4e0e..c25e762f4 100644 --- a/gui/builtinGraphs/fitWarpTime.py +++ b/gui/builtinGraphs/fitWarpTime.py @@ -21,6 +21,7 @@ import math from eos.const import FittingModuleState +from service.const import GraphCacheCleanupReason from .base import FitGraph, XDef, YDef, Input, FitDataCache @@ -33,8 +34,11 @@ class FitWarpTimeGraph(FitGraph): super().__init__() self._subspeedCache = SubwarpSpeedCache() - def _clearInternalCache(self, fitID): - self._subspeedCache.clear(fitID) + def _clearInternalCache(self, reason, extraData): + if reason == GraphCacheCleanupReason.fitChanged: + self._subspeedCache.clearForFit(extraData) + elif reason == GraphCacheCleanupReason.graphSwitched: + self._subspeedCache.clearAll() # UI stuff internalName = 'warpTimeGraph' diff --git a/gui/graphFrame/frame.py b/gui/graphFrame/frame.py index 0304a0722..064f067b1 100644 --- a/gui/graphFrame/frame.py +++ b/gui/graphFrame/frame.py @@ -31,6 +31,7 @@ import gui.globalEvents as GE import gui.mainFrame from gui.bitmap_loader import BitmapLoader from gui.builtinGraphs.base import FitGraph +from service.const import GraphCacheCleanupReason from service.settings import GraphSettings from .panel import GraphControlPanel @@ -152,18 +153,18 @@ class GraphFrame(wx.Frame): def OnFitChanged(self, event): event.Skip() - self.getView().clearCache(fitID=event.fitID) + self.clearCache(reason=GraphCacheCleanupReason.fitChanged, extraData=event.fitID) self.draw() def OnGraphOptionChanged(self, event): event.Skip() - self.getView().clearCache() + self.clearCache(reason=GraphCacheCleanupReason.optionChanged) self.draw() def OnGraphSwitched(self, event): view = self.getView() GraphSettings.getInstance().set('selectedGraph', view.internalName) - self.clearCache() + self.clearCache(reason=GraphCacheCleanupReason.graphSwitched) self.ctrlPanel.updateControls() self.draw() event.Skip() @@ -177,8 +178,8 @@ class GraphFrame(wx.Frame): def getView(self): return self.graphSelection.GetClientData(self.graphSelection.GetSelection()) - def clearCache(self, fitID=None): - self.getView().clearCache(fitID=fitID) + def clearCache(self, reason, extraData=None): + self.getView().clearCache(reason, extraData) def draw(self): global mpl_version diff --git a/gui/graphFrame/panel.py b/gui/graphFrame/panel.py index 3d5fd334a..8a82c6280 100644 --- a/gui/graphFrame/panel.py +++ b/gui/graphFrame/panel.py @@ -25,6 +25,7 @@ import wx from gui.bitmap_loader import BitmapLoader from gui.contextMenu import ContextMenu +from service.const import GraphCacheCleanupReason from service.fit import Fit from .input import ConstantBox, RangeBox from .lists import FitList, TargetList @@ -118,8 +119,8 @@ class GraphControlPanel(wx.Panel): self.SetSizer(mainSizer) - self.drawTimer = wx.Timer(self) - self.Bind(wx.EVT_TIMER, self.OnDrawTimer, self.drawTimer) + self.inputTimer = wx.Timer(self) + self.Bind(wx.EVT_TIMER, self.OnInputTimer, self.inputTimer) self._setVectorDefaults() def updateControls(self, layout=True): @@ -258,12 +259,12 @@ class GraphControlPanel(wx.Panel): def OnFieldChanged(self, event): event.Skip() - self.drawTimer.Stop() - self.drawTimer.Start(Fit.getInstance().serviceFittingOptions['marketSearchDelay'], True) + self.inputTimer.Stop() + self.inputTimer.Start(Fit.getInstance().serviceFittingOptions['marketSearchDelay'], True) - def OnDrawTimer(self, event): + def OnInputTimer(self, event): event.Skip() - self.graphFrame.clearCache() + self.graphFrame.clearCache(reason=GraphCacheCleanupReason.inputChanged) self.graphFrame.draw() def getValues(self): diff --git a/service/const.py b/service/const.py index 3348e37d5..436c3cdf1 100644 --- a/service/const.py +++ b/service/const.py @@ -117,3 +117,11 @@ class GraphDpsDroneMode(IntEnum): auto = 1 followAttacker = 2 followTarget = 3 + + +@unique +class GraphCacheCleanupReason(IntEnum): + fitChanged = auto() + graphSwitched = auto() + inputChanged = auto() + optionChanged = auto()