mirror of
https://github.com/osukey/osukey.git
synced 2025-05-03 20:57:28 +09:00
Merge branch 'master' into song-select-transition-improvement
This commit is contained in:
commit
c27f7fa421
@ -11,7 +11,7 @@
|
|||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\osu.Game.Rulesets.EmptyFreeform\osu.Game.Rulesets.EmptyFreeform.csproj" />
|
<ProjectReference Include="..\osu.Game.Rulesets.EmptyFreeform\osu.Game.Rulesets.EmptyFreeform.csproj" />
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\osu.Game.Rulesets.Pippidon\osu.Game.Rulesets.Pippidon.csproj" />
|
<ProjectReference Include="..\osu.Game.Rulesets.Pippidon\osu.Game.Rulesets.Pippidon.csproj" />
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\osu.Game.Rulesets.EmptyScrolling\osu.Game.Rulesets.EmptyScrolling.csproj" />
|
<ProjectReference Include="..\osu.Game.Rulesets.EmptyScrolling\osu.Game.Rulesets.EmptyScrolling.csproj" />
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\osu.Game.Rulesets.Pippidon\osu.Game.Rulesets.Pippidon.csproj" />
|
<ProjectReference Include="..\osu.Game.Rulesets.Pippidon\osu.Game.Rulesets.Pippidon.csproj" />
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
<PackageReference Include="Clowd.Squirrel" Version="2.9.42" />
|
<PackageReference Include="Clowd.Squirrel" Version="2.9.42" />
|
||||||
<PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
|
<PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
|
||||||
<PackageReference Include="System.IO.Packaging" Version="6.0.0" />
|
<PackageReference Include="System.IO.Packaging" Version="6.0.0" />
|
||||||
<PackageReference Include="DiscordRichPresence" Version="1.0.175" />
|
<PackageReference Include="DiscordRichPresence" Version="1.1.1.14" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Resources">
|
<ItemGroup Label="Resources">
|
||||||
<EmbeddedResource Include="lazer.ico" />
|
<EmbeddedResource Include="lazer.ico" />
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.2" />
|
<PackageReference Include="BenchmarkDotNet" Version="0.13.2" />
|
||||||
<PackageReference Include="nunit" Version="3.13.3" />
|
<PackageReference Include="nunit" Version="3.13.3" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
||||||
<PackageReference Include="Moq" Version="4.18.2" />
|
<PackageReference Include="Moq" Version="4.18.2" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
|
@ -15,7 +15,6 @@ using osu.Game.Rulesets.Osu.Objects;
|
|||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Osu.Skinning.Default;
|
using osu.Game.Rulesets.Osu.Skinning.Default;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Skinning.Argon
|
namespace osu.Game.Rulesets.Osu.Skinning.Argon
|
||||||
{
|
{
|
||||||
@ -51,6 +50,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
|
|||||||
private Container centre = null!;
|
private Container centre = null!;
|
||||||
private CircularContainer fill = null!;
|
private CircularContainer fill = null!;
|
||||||
|
|
||||||
|
private Container ticksContainer = null!;
|
||||||
|
private ArgonSpinnerTicks ticks = null!;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(DrawableHitObject drawableHitObject)
|
private void load(DrawableHitObject drawableHitObject)
|
||||||
{
|
{
|
||||||
@ -69,41 +71,85 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
fill = new CircularContainer
|
new Container
|
||||||
{
|
{
|
||||||
Name = @"Fill",
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Masking = true,
|
Padding = new MarginPadding(8f),
|
||||||
EdgeEffect = new EdgeEffectParameters
|
Children = new[]
|
||||||
{
|
{
|
||||||
Type = EdgeEffectType.Shadow,
|
fill = new CircularContainer
|
||||||
Colour = Colour4.FromHex("FC618F").Opacity(1f),
|
{
|
||||||
Radius = 40,
|
Name = @"Fill",
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Masking = true,
|
||||||
|
EdgeEffect = new EdgeEffectParameters
|
||||||
|
{
|
||||||
|
Type = EdgeEffectType.Shadow,
|
||||||
|
Colour = Colour4.FromHex("FC618F").Opacity(1f),
|
||||||
|
Radius = 40,
|
||||||
|
},
|
||||||
|
Child = new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Alpha = 0f,
|
||||||
|
AlwaysPresent = true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ticksContainer = new Container
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Child = ticks = new ArgonSpinnerTicks(),
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Child = new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Alpha = 0f,
|
|
||||||
AlwaysPresent = true,
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
new CircularContainer
|
new Container
|
||||||
{
|
{
|
||||||
Name = @"Ring",
|
Name = @"Ring",
|
||||||
Masking = true,
|
Masking = true,
|
||||||
BorderColour = Color4.White,
|
|
||||||
BorderThickness = 5,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Child = new Box
|
Padding = new MarginPadding(8f),
|
||||||
|
Children = new[]
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
new ArgonSpinnerRingArc
|
||||||
Alpha = 0,
|
{
|
||||||
AlwaysPresent = true,
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Name = "Top Arc",
|
||||||
|
},
|
||||||
|
new ArgonSpinnerRingArc
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Name = "Bottom Arc",
|
||||||
|
Scale = new Vector2(1, -1),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
Name = @"Sides",
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
new ArgonSpinnerProgressArc
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Name = "Left Bar"
|
||||||
|
},
|
||||||
|
new ArgonSpinnerProgressArc
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Name = "Right Bar",
|
||||||
|
Scale = new Vector2(-1, 1),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
new ArgonSpinnerTicks(),
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
centre = new Container
|
centre = new Container
|
||||||
@ -169,7 +215,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
|
|||||||
float targetScale = initial_fill_scale + (0.98f - initial_fill_scale) * drawableSpinner.Progress;
|
float targetScale = initial_fill_scale + (0.98f - initial_fill_scale) * drawableSpinner.Progress;
|
||||||
|
|
||||||
fill.Scale = new Vector2((float)Interpolation.Lerp(fill.Scale.X, targetScale, Math.Clamp(Math.Abs(Time.Elapsed) / 100, 0, 1)));
|
fill.Scale = new Vector2((float)Interpolation.Lerp(fill.Scale.X, targetScale, Math.Clamp(Math.Abs(Time.Elapsed) / 100, 0, 1)));
|
||||||
disc.Rotation = drawableSpinner.RotationTracker.Rotation;
|
ticks.Rotation = drawableSpinner.RotationTracker.Rotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state)
|
private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state)
|
||||||
@ -182,35 +228,16 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
|
|||||||
using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt))
|
using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt))
|
||||||
{
|
{
|
||||||
this.ScaleTo(initial_scale);
|
this.ScaleTo(initial_scale);
|
||||||
this.RotateTo(0);
|
ticksContainer.RotateTo(0);
|
||||||
|
centre.ScaleTo(0);
|
||||||
|
disc.ScaleTo(0);
|
||||||
|
|
||||||
using (BeginDelayedSequence(spinner.TimePreempt / 2))
|
using (BeginDelayedSequence(spinner.TimePreempt / 2))
|
||||||
{
|
{
|
||||||
// constant ambient rotation to give the spinner "spinning" character.
|
// constant ambient rotation to give the spinner "spinning" character.
|
||||||
this.RotateTo((float)(25 * spinner.Duration / 2000), spinner.TimePreempt + spinner.Duration);
|
ticksContainer.RotateTo((float)(25 * spinner.Duration / 2000), spinner.TimePreempt + spinner.Duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
using (BeginDelayedSequence(spinner.TimePreempt + spinner.Duration + drawableHitObject.Result.TimeOffset))
|
|
||||||
{
|
|
||||||
switch (state)
|
|
||||||
{
|
|
||||||
case ArmedState.Hit:
|
|
||||||
this.ScaleTo(initial_scale * 1.2f, 320, Easing.Out);
|
|
||||||
this.RotateTo(Rotation + 180, 320);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ArmedState.Miss:
|
|
||||||
this.ScaleTo(initial_scale * 0.8f, 320, Easing.In);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt))
|
|
||||||
{
|
|
||||||
centre.ScaleTo(0);
|
|
||||||
disc.ScaleTo(0);
|
|
||||||
|
|
||||||
using (BeginDelayedSequence(spinner.TimePreempt / 2))
|
using (BeginDelayedSequence(spinner.TimePreempt / 2))
|
||||||
{
|
{
|
||||||
centre.ScaleTo(0.3f, spinner.TimePreempt / 4, Easing.OutQuint);
|
centre.ScaleTo(0.3f, spinner.TimePreempt / 4, Easing.OutQuint);
|
||||||
@ -222,6 +249,21 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
|
|||||||
disc.ScaleTo(1, spinner.TimePreempt / 2, Easing.OutQuint);
|
disc.ScaleTo(1, spinner.TimePreempt / 2, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using (BeginDelayedSequence(spinner.TimePreempt + spinner.Duration + drawableHitObject.Result.TimeOffset))
|
||||||
|
{
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case ArmedState.Hit:
|
||||||
|
disc.ScaleTo(initial_scale * 1.2f, 320, Easing.Out);
|
||||||
|
ticksContainer.RotateTo(ticksContainer.Rotation + 180, 320);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ArmedState.Miss:
|
||||||
|
disc.ScaleTo(initial_scale * 0.8f, 320, Easing.In);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,71 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Skinning.Argon
|
||||||
|
{
|
||||||
|
public class ArgonSpinnerProgressArc : CompositeDrawable
|
||||||
|
{
|
||||||
|
private const float arc_fill = 0.15f;
|
||||||
|
private const float arc_radius = 0.12f;
|
||||||
|
|
||||||
|
private CircularProgress fill = null!;
|
||||||
|
|
||||||
|
private DrawableSpinner spinner = null!;
|
||||||
|
|
||||||
|
private CircularProgress background = null!;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(DrawableHitObject drawableHitObject)
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
spinner = (DrawableSpinner)drawableHitObject;
|
||||||
|
|
||||||
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
background = new CircularProgress
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Colour = Color4.White.Opacity(0.25f),
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Current = { Value = arc_fill },
|
||||||
|
Rotation = 90 - arc_fill * 180,
|
||||||
|
InnerRadius = arc_radius,
|
||||||
|
RoundedCaps = true,
|
||||||
|
},
|
||||||
|
fill = new CircularProgress
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
InnerRadius = arc_radius,
|
||||||
|
RoundedCaps = true,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
background.Alpha = spinner.Progress >= 1 ? 0 : 1;
|
||||||
|
|
||||||
|
fill.Alpha = (float)Interpolation.DampContinuously(fill.Alpha, spinner.Progress > 0 && spinner.Progress < 1 ? 1 : 0, 40f, (float)Math.Abs(Time.Elapsed));
|
||||||
|
fill.Current.Value = (float)Interpolation.DampContinuously(fill.Current.Value, spinner.Progress >= 1 ? 0 : arc_fill * spinner.Progress, 40f, (float)Math.Abs(Time.Elapsed));
|
||||||
|
|
||||||
|
fill.Rotation = (float)(90 - fill.Current.Value * 180);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
53
osu.Game.Rulesets.Osu/Skinning/Argon/ArgonSpinnerRingArc.cs
Normal file
53
osu.Game.Rulesets.Osu/Skinning/Argon/ArgonSpinnerRingArc.cs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Skinning.Argon
|
||||||
|
{
|
||||||
|
public class ArgonSpinnerRingArc : CompositeDrawable
|
||||||
|
{
|
||||||
|
private const float arc_fill = 0.31f;
|
||||||
|
private const float arc_fill_complete = 0.50f;
|
||||||
|
|
||||||
|
private const float arc_radius = 0.02f;
|
||||||
|
|
||||||
|
private DrawableSpinner spinner = null!;
|
||||||
|
private CircularProgress fill = null!;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(DrawableHitObject drawableHitObject)
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
spinner = (DrawableSpinner)drawableHitObject;
|
||||||
|
InternalChild = fill = new CircularProgress
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Current = { Value = arc_fill },
|
||||||
|
Rotation = -arc_fill * 180,
|
||||||
|
InnerRadius = arc_radius,
|
||||||
|
RoundedCaps = true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
fill.Current.Value = (float)Interpolation.DampContinuously(fill.Current.Value, spinner.Progress >= 1 ? arc_fill_complete : arc_fill, 40f, (float)Math.Abs(Time.Elapsed));
|
||||||
|
fill.InnerRadius = (float)Interpolation.DampContinuously(fill.InnerRadius, spinner.Progress >= 1 ? arc_radius * 2.2f : arc_radius, 40f, (float)Math.Abs(Time.Elapsed));
|
||||||
|
|
||||||
|
fill.Rotation = (float)(-fill.Current.Value * 180);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,7 +3,7 @@
|
|||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
|
190
osu.Game.Tests/Visual/Gameplay/TestSceneBezierConverter.cs
Normal file
190
osu.Game.Tests/Visual/Gameplay/TestSceneBezierConverter.cs
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Lines;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
|
{
|
||||||
|
public class TestSceneBezierConverter : OsuTestScene
|
||||||
|
{
|
||||||
|
private readonly SmoothPath drawablePath;
|
||||||
|
private readonly SmoothPath controlPointDrawablePath;
|
||||||
|
private readonly SmoothPath convertedDrawablePath;
|
||||||
|
private readonly SmoothPath convertedControlPointDrawablePath;
|
||||||
|
|
||||||
|
private SliderPath path = null!;
|
||||||
|
private SliderPath convertedPath = null!;
|
||||||
|
|
||||||
|
public TestSceneBezierConverter()
|
||||||
|
{
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
Children =
|
||||||
|
new Drawable[]
|
||||||
|
{
|
||||||
|
drawablePath = new SmoothPath(),
|
||||||
|
controlPointDrawablePath = new SmoothPath
|
||||||
|
{
|
||||||
|
Colour = Colour4.Magenta,
|
||||||
|
PathRadius = 1f
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Position = new Vector2(100)
|
||||||
|
},
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
Children =
|
||||||
|
new Drawable[]
|
||||||
|
{
|
||||||
|
convertedDrawablePath = new SmoothPath(),
|
||||||
|
convertedControlPointDrawablePath = new SmoothPath
|
||||||
|
{
|
||||||
|
Colour = Colour4.Magenta,
|
||||||
|
PathRadius = 1f
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Position = new Vector2(100, 300)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
resetPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup() => Schedule(resetPath);
|
||||||
|
|
||||||
|
private void resetPath()
|
||||||
|
{
|
||||||
|
path = new SliderPath();
|
||||||
|
convertedPath = new SliderPath();
|
||||||
|
|
||||||
|
path.Version.ValueChanged += getConvertedControlPoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void getConvertedControlPoints(ValueChangedEvent<int> obj)
|
||||||
|
{
|
||||||
|
convertedPath.ControlPoints.Clear();
|
||||||
|
convertedPath.ControlPoints.AddRange(BezierConverter.ConvertToModernBezier(path.ControlPoints));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
List<Vector2> vertices = new List<Vector2>();
|
||||||
|
|
||||||
|
path.GetPathToProgress(vertices, 0, 1);
|
||||||
|
|
||||||
|
drawablePath.Vertices = vertices;
|
||||||
|
controlPointDrawablePath.Vertices = path.ControlPoints.Select(o => o.Position).ToList();
|
||||||
|
|
||||||
|
if (controlPointDrawablePath.Vertices.Count > 0)
|
||||||
|
{
|
||||||
|
controlPointDrawablePath.Position =
|
||||||
|
drawablePath.PositionInBoundingBox(drawablePath.Vertices[0]) - controlPointDrawablePath.PositionInBoundingBox(controlPointDrawablePath.Vertices[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
vertices.Clear();
|
||||||
|
|
||||||
|
convertedPath.GetPathToProgress(vertices, 0, 1);
|
||||||
|
|
||||||
|
convertedDrawablePath.Vertices = vertices;
|
||||||
|
convertedControlPointDrawablePath.Vertices = convertedPath.ControlPoints.Select(o => o.Position).ToList();
|
||||||
|
|
||||||
|
if (convertedControlPointDrawablePath.Vertices.Count > 0)
|
||||||
|
{
|
||||||
|
convertedControlPointDrawablePath.Position = convertedDrawablePath.PositionInBoundingBox(convertedDrawablePath.Vertices[0])
|
||||||
|
- convertedControlPointDrawablePath.PositionInBoundingBox(convertedControlPointDrawablePath.Vertices[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestEmptyPath()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase(PathType.Linear)]
|
||||||
|
[TestCase(PathType.Bezier)]
|
||||||
|
[TestCase(PathType.Catmull)]
|
||||||
|
[TestCase(PathType.PerfectCurve)]
|
||||||
|
public void TestSingleSegment(PathType type)
|
||||||
|
=> AddStep("create path", () => path.ControlPoints.AddRange(createSegment(type, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
|
||||||
|
|
||||||
|
[TestCase(PathType.Linear)]
|
||||||
|
[TestCase(PathType.Bezier)]
|
||||||
|
[TestCase(PathType.Catmull)]
|
||||||
|
[TestCase(PathType.PerfectCurve)]
|
||||||
|
public void TestMultipleSegment(PathType type)
|
||||||
|
{
|
||||||
|
AddStep("create path", () =>
|
||||||
|
{
|
||||||
|
path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero));
|
||||||
|
path.ControlPoints.AddRange(createSegment(type, new Vector2(0, 100), new Vector2(100), Vector2.Zero));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestComplex()
|
||||||
|
{
|
||||||
|
AddStep("create path", () =>
|
||||||
|
{
|
||||||
|
path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(100, 0)));
|
||||||
|
path.ControlPoints.AddRange(createSegment(PathType.Bezier, new Vector2(100, 0), new Vector2(150, 30), new Vector2(100, 100)));
|
||||||
|
path.ControlPoints.AddRange(createSegment(PathType.PerfectCurve, new Vector2(100, 100), new Vector2(25, 50), Vector2.Zero));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase(0, 100)]
|
||||||
|
[TestCase(1, 100)]
|
||||||
|
[TestCase(5, 100)]
|
||||||
|
[TestCase(10, 100)]
|
||||||
|
[TestCase(30, 100)]
|
||||||
|
[TestCase(50, 100)]
|
||||||
|
[TestCase(100, 100)]
|
||||||
|
[TestCase(100, 1)]
|
||||||
|
public void TestPerfectCurveAngles(float height, float width)
|
||||||
|
{
|
||||||
|
AddStep("create path", () =>
|
||||||
|
{
|
||||||
|
path.ControlPoints.AddRange(createSegment(PathType.PerfectCurve, Vector2.Zero, new Vector2(width / 2, height), new Vector2(width, 0)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase(2)]
|
||||||
|
[TestCase(4)]
|
||||||
|
public void TestPerfectCurveFallbackScenarios(int points)
|
||||||
|
{
|
||||||
|
AddStep("create path", () =>
|
||||||
|
{
|
||||||
|
switch (points)
|
||||||
|
{
|
||||||
|
case 2:
|
||||||
|
path.ControlPoints.AddRange(createSegment(PathType.PerfectCurve, Vector2.Zero, new Vector2(0, 100)));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
path.ControlPoints.AddRange(createSegment(PathType.PerfectCurve, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0)));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<PathControlPoint> createSegment(PathType type, params Vector2[] controlPoints)
|
||||||
|
{
|
||||||
|
var points = controlPoints.Select(p => new PathControlPoint { Position = p }).ToList();
|
||||||
|
points[0].Type = type;
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,11 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<Import Project="..\osu.TestProject.props" />
|
<Import Project="..\osu.TestProject.props" />
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="DeepEqual" Version="4.1.0" />
|
<PackageReference Include="DeepEqual" Version="4.2.1" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
||||||
<PackageReference Include="Nito.AsyncEx" Version="5.1.2" />
|
<PackageReference Include="Nito.AsyncEx" Version="5.1.2" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" />
|
||||||
<PackageReference Include="Moq" Version="4.18.2" />
|
<PackageReference Include="Moq" Version="4.18.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
|
287
osu.Game/Rulesets/Objects/BezierConverter.cs
Normal file
287
osu.Game/Rulesets/Objects/BezierConverter.cs
Normal file
@ -0,0 +1,287 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Objects
|
||||||
|
{
|
||||||
|
public static class BezierConverter
|
||||||
|
{
|
||||||
|
private struct CircleBezierPreset
|
||||||
|
{
|
||||||
|
public readonly double ArcLength;
|
||||||
|
public readonly Vector2d[] ControlPoints;
|
||||||
|
|
||||||
|
public CircleBezierPreset(double arcLength, Vector2d[] controlPoints)
|
||||||
|
{
|
||||||
|
ArcLength = arcLength;
|
||||||
|
ControlPoints = controlPoints;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extremely accurate a bezier anchor positions for approximating circles of several arc lengths
|
||||||
|
private static readonly CircleBezierPreset[] circle_presets =
|
||||||
|
{
|
||||||
|
new CircleBezierPreset(0.4993379862754501,
|
||||||
|
new[] { new Vector2d(1, 0), new Vector2d(1, 0.2549893626632736f), new Vector2d(0.8778997558480327f, 0.47884446188920726f) }),
|
||||||
|
new CircleBezierPreset(1.7579419829169447,
|
||||||
|
new[] { new Vector2d(1, 0), new Vector2d(1, 0.6263026f), new Vector2d(0.42931178f, 1.0990661f), new Vector2d(-0.18605515f, 0.9825393f) }),
|
||||||
|
new CircleBezierPreset(3.1385246920140215,
|
||||||
|
new[] { new Vector2d(1, 0), new Vector2d(1, 0.87084764f), new Vector2d(0.002304826f, 1.5033062f), new Vector2d(-0.9973236f, 0.8739115f), new Vector2d(-0.9999953f, 0.0030679568f) }),
|
||||||
|
new CircleBezierPreset(5.69720464620727,
|
||||||
|
new[] { new Vector2d(1, 0), new Vector2d(1, 1.4137783f), new Vector2d(-1.4305235f, 2.0779421f), new Vector2d(-2.3410065f, -0.94017583f), new Vector2d(0.05132711f, -1.7309346f), new Vector2d(0.8331702f, -0.5530167f) }),
|
||||||
|
new CircleBezierPreset(2 * Math.PI,
|
||||||
|
new[] { new Vector2d(1, 0), new Vector2d(1, 1.2447058f), new Vector2d(-0.8526471f, 2.118367f), new Vector2d(-2.6211002f, 7.854936e-06f), new Vector2d(-0.8526448f, -2.118357f), new Vector2d(1, -1.2447058f), new Vector2d(1, 0) })
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a slider path to bezier control point positions compatible with the legacy osu! client.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="controlPoints">The control points of the path.</param>
|
||||||
|
/// <param name="position">The offset for the whole path.</param>
|
||||||
|
/// <returns>The list of legacy bezier control point positions.</returns>
|
||||||
|
public static List<Vector2> ConvertToLegacyBezier(IList<PathControlPoint> controlPoints, Vector2 position)
|
||||||
|
{
|
||||||
|
Vector2[] vertices = new Vector2[controlPoints.Count];
|
||||||
|
for (int i = 0; i < controlPoints.Count; i++)
|
||||||
|
vertices[i] = controlPoints[i].Position;
|
||||||
|
|
||||||
|
var result = new List<Vector2>();
|
||||||
|
int start = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < controlPoints.Count; i++)
|
||||||
|
{
|
||||||
|
if (controlPoints[i].Type == null && i < controlPoints.Count - 1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// The current vertex ends the segment
|
||||||
|
var segmentVertices = vertices.AsSpan().Slice(start, i - start + 1);
|
||||||
|
var segmentType = controlPoints[start].Type ?? PathType.Linear;
|
||||||
|
|
||||||
|
switch (segmentType)
|
||||||
|
{
|
||||||
|
case PathType.Catmull:
|
||||||
|
result.AddRange(from segment in ConvertCatmullToBezierAnchors(segmentVertices) from v in segment select v + position);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PathType.Linear:
|
||||||
|
result.AddRange(from segment in ConvertLinearToBezierAnchors(segmentVertices) from v in segment select v + position);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PathType.PerfectCurve:
|
||||||
|
result.AddRange(ConvertCircleToBezierAnchors(segmentVertices).Select(v => v + position));
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
foreach (Vector2 v in segmentVertices)
|
||||||
|
{
|
||||||
|
result.Add(v + position);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the new segment at the current vertex
|
||||||
|
start = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a path of control points to an identical path using only Bezier type control points.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="controlPoints">The control points of the path.</param>
|
||||||
|
/// <returns>The list of bezier control points.</returns>
|
||||||
|
public static List<PathControlPoint> ConvertToModernBezier(IList<PathControlPoint> controlPoints)
|
||||||
|
{
|
||||||
|
Vector2[] vertices = new Vector2[controlPoints.Count];
|
||||||
|
for (int i = 0; i < controlPoints.Count; i++)
|
||||||
|
vertices[i] = controlPoints[i].Position;
|
||||||
|
|
||||||
|
var result = new List<PathControlPoint>();
|
||||||
|
int start = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < controlPoints.Count; i++)
|
||||||
|
{
|
||||||
|
if (controlPoints[i].Type == null && i < controlPoints.Count - 1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// The current vertex ends the segment
|
||||||
|
var segmentVertices = vertices.AsSpan().Slice(start, i - start + 1);
|
||||||
|
var segmentType = controlPoints[start].Type ?? PathType.Linear;
|
||||||
|
|
||||||
|
switch (segmentType)
|
||||||
|
{
|
||||||
|
case PathType.Catmull:
|
||||||
|
foreach (var segment in ConvertCatmullToBezierAnchors(segmentVertices))
|
||||||
|
{
|
||||||
|
for (int j = 0; j < segment.Length - 1; j++)
|
||||||
|
{
|
||||||
|
result.Add(new PathControlPoint(segment[j], j == 0 ? PathType.Bezier : null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PathType.Linear:
|
||||||
|
foreach (var segment in ConvertLinearToBezierAnchors(segmentVertices))
|
||||||
|
{
|
||||||
|
for (int j = 0; j < segment.Length - 1; j++)
|
||||||
|
{
|
||||||
|
result.Add(new PathControlPoint(segment[j], j == 0 ? PathType.Bezier : null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PathType.PerfectCurve:
|
||||||
|
var circleResult = ConvertCircleToBezierAnchors(segmentVertices);
|
||||||
|
|
||||||
|
for (int j = 0; j < circleResult.Length - 1; j++)
|
||||||
|
{
|
||||||
|
result.Add(new PathControlPoint(circleResult[j], j == 0 ? PathType.Bezier : null));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
for (int j = 0; j < segmentVertices.Length - 1; j++)
|
||||||
|
{
|
||||||
|
result.Add(new PathControlPoint(segmentVertices[j], j == 0 ? PathType.Bezier : null));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the new segment at the current vertex
|
||||||
|
start = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Add(new PathControlPoint(controlPoints[^1].Position));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts perfect curve anchors to bezier anchors.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="controlPoints">The control point positions to convert.</param>
|
||||||
|
public static Vector2[] ConvertCircleToBezierAnchors(ReadOnlySpan<Vector2> controlPoints)
|
||||||
|
{
|
||||||
|
if (controlPoints.Length != 3)
|
||||||
|
return controlPoints.ToArray();
|
||||||
|
|
||||||
|
var pr = new CircularArcProperties(controlPoints);
|
||||||
|
if (!pr.IsValid)
|
||||||
|
return controlPoints.ToArray();
|
||||||
|
|
||||||
|
CircleBezierPreset preset = circle_presets.Last();
|
||||||
|
|
||||||
|
foreach (CircleBezierPreset cbp in circle_presets)
|
||||||
|
{
|
||||||
|
if (cbp.ArcLength < pr.ThetaRange) continue;
|
||||||
|
|
||||||
|
preset = cbp;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
double arcLength = preset.ArcLength;
|
||||||
|
var arc = new Vector2d[preset.ControlPoints.Length];
|
||||||
|
preset.ControlPoints.CopyTo(arc, 0);
|
||||||
|
|
||||||
|
// Converge on arcLength of thetaRange
|
||||||
|
int n = arc.Length - 1;
|
||||||
|
double tf = pr.ThetaRange / arcLength;
|
||||||
|
|
||||||
|
while (Math.Abs(tf - 1) > 1E-7)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < n; j++)
|
||||||
|
{
|
||||||
|
for (int i = n; i > j; i--)
|
||||||
|
{
|
||||||
|
arc[i] = arc[i] * tf + arc[i - 1] * (1 - tf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
arcLength = Math.Atan2(arc.Last()[1], arc.Last()[0]);
|
||||||
|
|
||||||
|
if (arcLength < 0)
|
||||||
|
{
|
||||||
|
arcLength += 2 * Math.PI;
|
||||||
|
}
|
||||||
|
|
||||||
|
tf = pr.ThetaRange / arcLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust rotation, radius, and position
|
||||||
|
var result = new Vector2[arc.Length];
|
||||||
|
|
||||||
|
for (int i = 0; i < arc.Length; i++)
|
||||||
|
{
|
||||||
|
result[i] = new Vector2(
|
||||||
|
(float)((Math.Cos(pr.ThetaStart) * arc[i].X + -Math.Sin(pr.ThetaStart) * pr.Direction * arc[i].Y) * pr.Radius + pr.Centre.X),
|
||||||
|
(float)((Math.Sin(pr.ThetaStart) * arc[i].X + Math.Cos(pr.ThetaStart) * pr.Direction * arc[i].Y) * pr.Radius + pr.Centre.Y));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts catmull anchors to bezier anchors.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="controlPoints">The control point positions to convert.</param>
|
||||||
|
public static Vector2[][] ConvertCatmullToBezierAnchors(ReadOnlySpan<Vector2> controlPoints)
|
||||||
|
{
|
||||||
|
int iLen = controlPoints.Length;
|
||||||
|
var bezier = new Vector2[iLen - 1][];
|
||||||
|
|
||||||
|
for (int i = 0; i < iLen - 1; i++)
|
||||||
|
{
|
||||||
|
var v1 = i > 0 ? controlPoints[i - 1] : controlPoints[i];
|
||||||
|
var v2 = controlPoints[i];
|
||||||
|
var v3 = i < iLen - 1 ? controlPoints[i + 1] : v2 + v2 - v1;
|
||||||
|
var v4 = i < iLen - 2 ? controlPoints[i + 2] : v3 + v3 - v2;
|
||||||
|
|
||||||
|
bezier[i] = new[]
|
||||||
|
{
|
||||||
|
v2,
|
||||||
|
(-v1 + 6 * v2 + v3) / 6,
|
||||||
|
(-v4 + 6 * v3 + v2) / 6,
|
||||||
|
v3
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return bezier;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts linear anchors to bezier anchors.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="controlPoints">The control point positions to convert.</param>
|
||||||
|
public static Vector2[][] ConvertLinearToBezierAnchors(ReadOnlySpan<Vector2> controlPoints)
|
||||||
|
{
|
||||||
|
int iLen = controlPoints.Length;
|
||||||
|
var bezier = new Vector2[iLen - 1][];
|
||||||
|
|
||||||
|
for (int i = 0; i < iLen - 1; i++)
|
||||||
|
{
|
||||||
|
bezier[i] = new[]
|
||||||
|
{
|
||||||
|
controlPoints[i],
|
||||||
|
controlPoints[i + 1]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return bezier;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -23,10 +23,10 @@
|
|||||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.46" />
|
<PackageReference Include="HtmlAgilityPack" Version="1.11.46" />
|
||||||
<PackageReference Include="Humanizer" Version="2.14.1" />
|
<PackageReference Include="Humanizer" Version="2.14.1" />
|
||||||
<PackageReference Include="MessagePack" Version="2.4.35" />
|
<PackageReference Include="MessagePack" Version="2.4.35" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="6.0.9" />
|
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="6.0.10" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="6.0.9" />
|
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="6.0.10" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="6.0.9" />
|
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="6.0.10" />
|
||||||
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="6.0.9" />
|
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="6.0.10" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
|
||||||
<PackageReference Include="Microsoft.Toolkit.HighPerformance" Version="7.1.2" />
|
<PackageReference Include="Microsoft.Toolkit.HighPerformance" Version="7.1.2" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||||
@ -34,10 +34,10 @@
|
|||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Realm" Version="10.17.0" />
|
<PackageReference Include="Realm" Version="10.18.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2022.1101.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2022.1101.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.1103.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.1103.0" />
|
||||||
<PackageReference Include="Sentry" Version="3.22.0" />
|
<PackageReference Include="Sentry" Version="3.23.1" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.32.2" />
|
<PackageReference Include="SharpCompress" Version="0.32.2" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||||
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.2" />
|
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.2" />
|
||||||
|
Loading…
x
Reference in New Issue
Block a user