mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 09:32:55 +08:00
Move path type correction
This is better because `PathControlPointVisualizer` is local to the editor, meaning there is no chance that this could affect gameplay.
This commit is contained in:
parent
0bcd38e661
commit
4ae3eaaac6
@ -91,6 +91,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
Connections.Add(new PathControlPointConnectionPiece(slider, e.NewStartingIndex + i));
|
Connections.Add(new PathControlPointConnectionPiece(slider, e.NewStartingIndex + i));
|
||||||
|
|
||||||
|
point.Changed += updatePathTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -100,6 +102,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
{
|
{
|
||||||
Pieces.RemoveAll(p => p.ControlPoint == point);
|
Pieces.RemoveAll(p => p.ControlPoint == point);
|
||||||
Connections.RemoveAll(c => c.ControlPoint == point);
|
Connections.RemoveAll(c => c.ControlPoint == point);
|
||||||
|
|
||||||
|
point.Changed -= updatePathTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If removing before the end of the path,
|
// If removing before the end of the path,
|
||||||
@ -142,6 +146,56 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles correction of invalid path types.
|
||||||
|
/// </summary>
|
||||||
|
private void updatePathTypes()
|
||||||
|
{
|
||||||
|
foreach (PathControlPoint segmentStartPoint in slider.Path.ControlPoints.Where(p => p.Type.Value != null))
|
||||||
|
{
|
||||||
|
if (segmentStartPoint.Type.Value != PathType.PerfectCurve)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Vector2[] points = slider.Path.PointsInSegment(segmentStartPoint).Select(p => p.Position.Value).ToArray();
|
||||||
|
if (points.Length == 3 && !validCircularArcSegment(points))
|
||||||
|
segmentStartPoint.Type.Value = PathType.Bezier;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns whether the given points are arranged in a valid way. Invalid if points
|
||||||
|
/// are almost entirely linear - as this causes the radius to approach infinity,
|
||||||
|
/// which would exhaust memory when drawing / approximating.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="points">The three points that make up this circular arc segment.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private bool validCircularArcSegment(IReadOnlyList<Vector2> points)
|
||||||
|
{
|
||||||
|
Vector2 a = points[0];
|
||||||
|
Vector2 b = points[1];
|
||||||
|
Vector2 c = points[2];
|
||||||
|
|
||||||
|
float maxLength = points.Max(p => p.Length);
|
||||||
|
|
||||||
|
Vector2 normA = new Vector2(a.X / maxLength, a.Y / maxLength);
|
||||||
|
Vector2 normB = new Vector2(b.X / maxLength, b.Y / maxLength);
|
||||||
|
Vector2 normC = new Vector2(c.X / maxLength, c.Y / maxLength);
|
||||||
|
|
||||||
|
float det = (normA.X - normB.X) * (normB.Y - normC.Y) - (normB.X - normC.X) * (normA.Y - normB.Y);
|
||||||
|
|
||||||
|
float acSq = (a - c).LengthSquared;
|
||||||
|
float abSq = (a - b).LengthSquared;
|
||||||
|
float bcSq = (b - c).LengthSquared;
|
||||||
|
|
||||||
|
// Exterior = curve wraps around the long way between end-points
|
||||||
|
// Exterior bottleneck is drawing-related, interior bottleneck is approximation-related,
|
||||||
|
// where the latter is much faster, hence differing thresholds
|
||||||
|
bool exterior = abSq > acSq || bcSq > acSq;
|
||||||
|
float threshold = exterior ? 0.05f : 0.001f;
|
||||||
|
|
||||||
|
return Math.Abs(det) >= threshold;
|
||||||
|
}
|
||||||
|
|
||||||
private void selectPiece(PathControlPointPiece piece, MouseButtonEvent e)
|
private void selectPiece(PathControlPointPiece piece, MouseButtonEvent e)
|
||||||
{
|
{
|
||||||
if (e.Button == MouseButton.Left && inputManager.CurrentState.Keyboard.ControlPressed)
|
if (e.Button == MouseButton.Left && inputManager.CurrentState.Keyboard.ControlPressed)
|
||||||
|
@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Objects
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invoked when any property of this <see cref="PathControlPoint"/> is changed.
|
/// Invoked when any property of this <see cref="PathControlPoint"/> is changed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal event Action Changed;
|
public event Action Changed;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new <see cref="PathControlPoint"/>.
|
/// Creates a new <see cref="PathControlPoint"/>.
|
||||||
|
@ -54,21 +54,13 @@ namespace osu.Game.Rulesets.Objects
|
|||||||
{
|
{
|
||||||
case NotifyCollectionChangedAction.Add:
|
case NotifyCollectionChangedAction.Add:
|
||||||
foreach (var c in args.NewItems.Cast<PathControlPoint>())
|
foreach (var c in args.NewItems.Cast<PathControlPoint>())
|
||||||
{
|
|
||||||
c.Changed += invalidate;
|
c.Changed += invalidate;
|
||||||
c.Changed += updatePathTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NotifyCollectionChangedAction.Reset:
|
case NotifyCollectionChangedAction.Reset:
|
||||||
case NotifyCollectionChangedAction.Remove:
|
case NotifyCollectionChangedAction.Remove:
|
||||||
foreach (var c in args.OldItems.Cast<PathControlPoint>())
|
foreach (var c in args.OldItems.Cast<PathControlPoint>())
|
||||||
{
|
|
||||||
c.Changed -= invalidate;
|
c.Changed -= invalidate;
|
||||||
c.Changed -= updatePathTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,56 +189,6 @@ namespace osu.Game.Rulesets.Objects
|
|||||||
return pointsInCurrentSegment;
|
return pointsInCurrentSegment;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Handles correction of invalid path types.
|
|
||||||
/// </summary>
|
|
||||||
private void updatePathTypes()
|
|
||||||
{
|
|
||||||
foreach (PathControlPoint segmentStartPoint in ControlPoints.Where(p => p.Type.Value != null))
|
|
||||||
{
|
|
||||||
if (segmentStartPoint.Type.Value != PathType.PerfectCurve)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
Vector2[] points = PointsInSegment(segmentStartPoint).Select(p => p.Position.Value).ToArray();
|
|
||||||
if (points.Length == 3 && !validCircularArcSegment(points))
|
|
||||||
segmentStartPoint.Type.Value = PathType.Bezier;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns whether the given points are arranged in a valid way. Invalid if points
|
|
||||||
/// are almost entirely linear - as this causes the radius to approach infinity,
|
|
||||||
/// which would exhaust memory when drawing / approximating.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="points">The three points that make up this circular arc segment.</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private bool validCircularArcSegment(IReadOnlyList<Vector2> points)
|
|
||||||
{
|
|
||||||
Vector2 a = points[0];
|
|
||||||
Vector2 b = points[1];
|
|
||||||
Vector2 c = points[2];
|
|
||||||
|
|
||||||
float maxLength = points.Max(p => p.Length);
|
|
||||||
|
|
||||||
Vector2 normA = new Vector2(a.X / maxLength, a.Y / maxLength);
|
|
||||||
Vector2 normB = new Vector2(b.X / maxLength, b.Y / maxLength);
|
|
||||||
Vector2 normC = new Vector2(c.X / maxLength, c.Y / maxLength);
|
|
||||||
|
|
||||||
float det = (normA.X - normB.X) * (normB.Y - normC.Y) - (normB.X - normC.X) * (normA.Y - normB.Y);
|
|
||||||
|
|
||||||
float acSq = (a - c).LengthSquared;
|
|
||||||
float abSq = (a - b).LengthSquared;
|
|
||||||
float bcSq = (b - c).LengthSquared;
|
|
||||||
|
|
||||||
// Exterior = curve wraps around the long way between end-points
|
|
||||||
// Exterior bottleneck is drawing-related, interior bottleneck is approximation-related,
|
|
||||||
// where the latter is much faster, hence differing thresholds
|
|
||||||
bool exterior = abSq > acSq || bcSq > acSq;
|
|
||||||
float threshold = exterior ? 0.05f : 0.001f;
|
|
||||||
|
|
||||||
return Math.Abs(det) >= threshold;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void invalidate()
|
private void invalidate()
|
||||||
{
|
{
|
||||||
pathCache.Invalidate();
|
pathCache.Invalidate();
|
||||||
|
Loading…
Reference in New Issue
Block a user