Skip to content

Commit

Permalink
Merge pull request #210 from Givikap120/straingraph_lag_fix
Browse files Browse the repository at this point in the history
Fix Strain Graph lag on long maps
  • Loading branch information
smoogipoo authored Jul 25, 2024
2 parents 68994d6 + 07ab92d commit a1bb55f
Show file tree
Hide file tree
Showing 2 changed files with 175 additions and 99 deletions.
263 changes: 164 additions & 99 deletions PerformanceCalculatorGUI/Components/StrainVisualizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,54 +23,6 @@

namespace PerformanceCalculatorGUI.Components
{
public partial class StrainBar : Bar, IHasTooltip
{
public StrainBar(string tooltip)
{
TooltipText = tooltip;
}

public LocalisableString TooltipText { get; }
}

public partial class StrainBarGraph : FillFlowContainer<StrainBar>
{
/// <summary>
/// Manually sets the max value, if null <see cref="Enumerable.Max(IEnumerable{float})"/> is instead used
/// </summary>
public float? MaxValue { get; set; }

/// <summary>
/// A list of floats that defines the length of each <see cref="Bar"/>
/// </summary>
public IEnumerable<(float val, string tooltip)> Values
{
set
{
Clear();

foreach (var (val, tooltip) in value)
{
float length = MaxValue ?? value.Max(x => x.val);
if (length != 0)
length = val / length;

float size = value.Count();
if (size != 0)
size = 1.0f / size;

Add(new StrainBar(tooltip)
{
RelativeSizeAxes = Axes.Both,
Size = new Vector2(size, 1),
Length = length,
Direction = BarDirection.BottomToTop
});
}
}
}
}

public partial class StrainVisualizer : Container
{
public readonly Bindable<Skill[]> Skills = new Bindable<Skill[]>();
Expand All @@ -93,6 +45,8 @@ public StrainVisualizer()
AutoSizeAxes = Axes.Y;
}

private float graphAlpha;

private void updateGraphs(ValueChangedEvent<Skill[]> val)
{
graphsContainer.Clear();
Expand All @@ -107,62 +61,18 @@ private void updateGraphs(ValueChangedEvent<Skill[]> val)
return;
}

var graphAlpha = Math.Min(1.5f / skills.Length, 0.9f);

List<(float val, string tooltip)[]> strainLists = new List<(float val, string tooltip)[]>();

foreach (var skill in skills)
{
var strains = ((StrainSkill)skill).GetCurrentStrainPeaks().ToArray();

var skillStrainList = new List<(float val, string tooltip)>();

for (int i = 0; i < strains.Length; i++)
{
var strain = strains[i];
var strainTime = TimeSpan.FromMilliseconds(TimeUntilFirstStrain.Value + i * 400); // 400 is strain length in StrainSkill
var tooltipText = $"~{strainTime:mm\\:ss\\.ff}";
skillStrainList.Add(((float)strain, tooltipText));
}

strainLists.Add(skillStrainList.ToArray());
}

var strainMaxValue = strainLists.Max(list => list.Max(x => x.val));

for (int i = 0; i < skills.Length; i++)
{
graphsContainer.AddRange(new Drawable[]
{
new BufferedContainer(cachedFrameBuffer: true)
{
RelativeSizeAxes = Axes.Both,
Alpha = graphAlpha,
Colour = skillColours[i],
Child = new StrainBarGraph
{
RelativeSizeAxes = Axes.Both,
MaxValue = strainMaxValue,
Values = strainLists[i]
}
}
});
}

graphsContainer.Add(new OsuSpriteText
{
Font = OsuFont.GetFont(size: 10),
Text = $"{strainMaxValue:0.00}"
});
graphAlpha = Math.Min(1.5f / skills.Length, 0.9f);
var strainLists = getStrainLists(skills);
addStrainBars(skills, strainLists);
addTooltipBars(strainLists);

if (val.OldValue == null || !val.NewValue.All(x => val.OldValue.Any(y => y.GetType().Name == x.GetType().Name)))
{
// skill list changed - recreate toggles
legendContainer.Clear();
graphToggles.Clear();

// we do Children - 1 because max strain value is in the graphsContainer too and we don't want it to have a toggle
for (int i = 0; i < graphsContainer.Children.Count - 1; i++)
for (int i = 0; i < skills.Length; i++)
{
// this is ugly, but it works
var graphToggleBindable = new Bindable<bool>();
Expand Down Expand Up @@ -209,8 +119,7 @@ private void updateGraphs(ValueChangedEvent<Skill[]> val)
}
else
{
// skill list is the same, keep graph toggles
for (int i = 0; i < graphsContainer.Children.Count - 1; i++)
for (int i = 0; i < skills.Length; i++)
{
// graphs are visible by default, we want to hide ones that were disabled before
if (!graphToggles[i].Value)
Expand Down Expand Up @@ -274,5 +183,161 @@ private void load(OsuColour colours)

Skills.BindValueChanged(updateGraphs);
}

private void addStrainBars(Skill[] skills, List<float[]> strainLists)
{
var strainMaxValue = strainLists.Max(list => list.Max());

for (int i = 0; i < skills.Length; i++)
{
graphsContainer.AddRange(new Drawable[]
{
new BufferedContainer(cachedFrameBuffer: true)
{
RelativeSizeAxes = Axes.Both,
Alpha = graphAlpha,
Colour = skillColours[i],
Child = new StrainBarGraph
{
RelativeSizeAxes = Axes.Both,
MaxValue = strainMaxValue,
Values = strainLists[i]
}
}
});
}

graphsContainer.Add(new OsuSpriteText
{
Font = OsuFont.GetFont(size: 10),
Text = $"{strainMaxValue:0.00}"
});
}

private void addTooltipBars(List<float[]> strainLists, int nBars = 1000)
{
double lastStrainTime = strainLists.Max(l => l.Length) * 400;

var tooltipList = new List<string>();

for (int i = 0; i < nBars; i++)
{
var strainTime = TimeSpan.FromMilliseconds(TimeUntilFirstStrain.Value + lastStrainTime * i / nBars);
var tooltipText = $"~{strainTime:mm\\:ss\\.ff}";
tooltipList.Add(tooltipText);
}

graphsContainer.AddRange(new Drawable[]
{
new BufferedContainer(cachedFrameBuffer: true)
{
RelativeSizeAxes = Axes.Both,
Alpha = 1,
Child = new TooltipBarGraph
{
RelativeSizeAxes = Axes.Both,
Values = tooltipList
}
}
});
}

private static List<float[]> getStrainLists(Skill[] skills)
{
List<float[]> strainLists = new List<float[]>();

foreach (var skill in skills)
{
var strains = ((StrainSkill)skill).GetCurrentStrainPeaks().ToArray();

var skillStrainList = new List<float>();

for (int i = 0; i < strains.Length; i++)
{
var strain = strains[i];
skillStrainList.Add(((float)strain));
}

strainLists.Add(skillStrainList.ToArray());
}

return strainLists;
}
}

public partial class StrainBarGraph : FillFlowContainer<Bar>
{
/// <summary>
/// Manually sets the max value, if null <see cref="Enumerable.Max(IEnumerable{float})"/> is instead used
/// </summary>
public float? MaxValue { get; set; }

/// <summary>
/// A list of floats that defines the length of each <see cref="Bar"/>
/// </summary>
public IEnumerable<float> Values
{
set
{
Clear();

foreach (var val in value)
{
float length = MaxValue ?? value.Max();
if (length != 0)
length = val / length;

float size = value.Count();
if (size != 0)
size = 1.0f / size;

Add(new Bar
{
RelativeSizeAxes = Axes.Both,
Size = new Vector2(size, 1),
Length = length,
Direction = BarDirection.BottomToTop
});
}
}
}
}

public partial class TooltipBar : Bar, IHasTooltip
{
public TooltipBar(string tooltip)
{
TooltipText = tooltip;
}

public LocalisableString TooltipText { get; }
}

public partial class TooltipBarGraph : FillFlowContainer<TooltipBar>
{
/// <summary>
/// A list of strings that defines tooltips, don't make it too big
/// </summary>
public IEnumerable<string> Values
{
set
{
Clear();

foreach (var tooltip in value)
{
float size = value.Count();
if (size != 0)
size = 1.0f / size;

Add(new TooltipBar(tooltip)
{
RelativeSizeAxes = Axes.Both,
Size = new Vector2(size, 1),
Direction = BarDirection.BottomToTop
});
}
}
}
}
}
11 changes: 11 additions & 0 deletions osu.Tools.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -769,9 +769,19 @@ See the LICENCE file in the repository root for full licence text.&#xD;
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticReadonly/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=StaticReadonly/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=TypeParameters/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=15b5b1f1_002D457c_002D4ca6_002Db278_002D5615aedc07d3/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static readonly fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="READONLY_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=236f7aa5_002D7b06_002D43ca_002Dbf2a_002D9b31bfcff09a/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Private" Description="Constant fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="CONSTANT_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=2c62818f_002D621b_002D4425_002Dadc9_002D78611099bfcb/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Any" Description="Type parameters"&gt;&lt;ElementKinds&gt;&lt;Kind Name="TYPE_PARAMETER" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=4a98fdf6_002D7d98_002D4f5a_002Dafeb_002Dea44ad98c70c/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Instance" AccessRightKinds="Private" Description="Instance fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="FIELD" /&gt;&lt;Kind Name="READONLY_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb"&gt;&lt;ExtraRule Prefix="_" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=669e5282_002Dfb4b_002D4e90_002D91e7_002D07d269d04b60/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Constant fields (not private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="CONSTANT_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=76f79b1e_002Dece7_002D4df2_002Da322_002D1bd7fea25eb7/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Any" Description="Local functions"&gt;&lt;ElementKinds&gt;&lt;Kind Name="LOCAL_FUNCTION" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=8b8504e3_002Df0be_002D4c14_002D9103_002Dc732f2bddc15/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Any" Description="Enum members"&gt;&lt;ElementKinds&gt;&lt;Kind Name="ENUM_MEMBER" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="False" Prefix="" Suffix="" Style="AaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=9d1af99b_002Dbefe_002D48a4_002D9eb3_002D661384e29869/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static, Instance" AccessRightKinds="Private" Description="private methods"&gt;&lt;ElementKinds&gt;&lt;Kind Name="ASYNC_METHOD" /&gt;&lt;Kind Name="METHOD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=9ffbe43b_002Dc610_002D411b_002D9839_002D1416a146d9b0/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static, Instance" AccessRightKinds="Protected, ProtectedInternal, Internal, Public" Description="internal/protected/public methods"&gt;&lt;ElementKinds&gt;&lt;Kind Name="ASYNC_METHOD" /&gt;&lt;Kind Name="METHOD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=a4c2df6c_002Db202_002D48d5_002Db077_002De678cb548c25/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static, Instance" AccessRightKinds="Private" Description="private properties"&gt;&lt;ElementKinds&gt;&lt;Kind Name="PROPERTY" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=a4f433b8_002Dabcd_002D4e55_002Da08f_002D82e78cef0f0c/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Any" Description="Local constants"&gt;&lt;ElementKinds&gt;&lt;Kind Name="LOCAL_CONSTANT" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=c873eafb_002Dd57f_002D481d_002D8c93_002D77f6863c2f88/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Static readonly fields (not private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="READONLY_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=f9fce829_002De6f4_002D4cb2_002D80f1_002D5497c44f51df/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=fd562728_002Dc23d_002D417f_002Da19f_002D9d854247fbea/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static, Instance" AccessRightKinds="Protected, ProtectedInternal, Internal, Public" Description="internal/protected/public properties"&gt;&lt;ElementKinds&gt;&lt;Kind Name="PROPERTY" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FBLOCK_005FSCOPE_005FCONSTANT/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FBLOCK_005FSCOPE_005FFUNCTION/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
Expand Down Expand Up @@ -836,6 +846,7 @@ See the LICENCE file in the repository root for full licence text.&#xD;
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EPredefinedNamingRulesToUserRulesUpgrade/@EntryIndexedValue">True</s:Boolean>
<s:String x:Key="/Default/Environment/UnitTesting/NUnitProvider/SetCurrentDirectoryTo/@EntryValue">TestFolder</s:String>
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=28A2A5FC43E07C488A4BC7430879479E/@KeyIndexDefined">True</s:Boolean>
<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=28A2A5FC43E07C488A4BC7430879479E/Applicability/=Live/@EntryIndexedValue">True</s:Boolean>
Expand Down

0 comments on commit a1bb55f

Please sign in to comment.