Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Strain Graph lag on long maps #210

Merged
merged 2 commits into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading