private bool PrevOverSpeedLimit(Anchor anchor) { if (anchor.PreviousAnchor == null) { return(false); } var diff = anchor.Pos - anchor.PreviousAnchor.Pos; if (ViewModel.GraphModeSetting == SlideratorVm.GraphMode.Position) { return(Math.Abs(InterpolatorHelper.GetBiggestDerivative(anchor.Interpolator) * diff.Y / diff.X) / ViewModel.SvGraphMultiplier > ViewModel.VelocityLimit); } return(Math.Abs(InterpolatorHelper.GetBiggestValue(anchor.Interpolator)) > ViewModel.VelocityLimit); }
private void AnchorsOnAnchorsChanged(object sender, DependencyPropertyChangedEventArgs e) { if (_ignoreAnchorsChange) { return; } var anchor = (Anchor)sender; // Correct the anchor change if it resulted in a speed limit violation if (PrevOverSpeedLimit(anchor) || NextOverSpeedLimit(anchor)) { _ignoreAnchorsChange = true; Graph.IgnoreAnchorUpdates = true; // Use binary search to find the closest value to the limit const double d = 0.001; switch (e.NewValue) { case double newDouble: var oldDouble = (double)e.OldValue; // Test if the old value is also a illegal speed violation anchor.SetValue(e.Property, oldDouble); if (PrevOverSpeedLimit(anchor) || NextOverSpeedLimit(anchor)) { anchor.SetValue(e.Property, newDouble); break; } anchor.SetValue(e.Property, BinarySearchUtil.DoubleBinarySearch( oldDouble, newDouble, d, mid => { anchor.SetValue(e.Property, mid); return(!PrevOverSpeedLimit(anchor) && !NextOverSpeedLimit(anchor)); })); break; case Vector2 newVector2: if (ViewModel.GraphModeSetting == SlideratorVm.GraphMode.Position && anchor.PreviousAnchor != null) { // List of bounds. X represents the minimum Y value and Y represents the maximum Y value // I use Vector2 here because it has useful math methods var bounds = new List <Vector2>(); if (anchor.PreviousAnchor != null) { var maxSpeed = InterpolatorHelper.GetBiggestDerivative(anchor.Interpolator); if (Math.Abs(newVector2.X - anchor.PreviousAnchor.Pos.X) < Precision.DOUBLE_EPSILON) { bounds.Add(new Vector2(anchor.PreviousAnchor.Pos.Y)); } else { bounds.Add(new Vector2(anchor.PreviousAnchor.Pos.Y) + new Vector2(Precision.DOUBLE_EPSILON).PerpendicularRight + new Vector2(ViewModel.VelocityLimit * ViewModel.SvGraphMultiplier * (newVector2.X - anchor.PreviousAnchor.Pos.X) / maxSpeed) .PerpendicularLeft); } } if (anchor.NextAnchor != null) { var maxSpeed = InterpolatorHelper.GetBiggestDerivative(anchor.NextAnchor.Interpolator); if (Math.Abs(newVector2.X - anchor.NextAnchor.Pos.X) < Precision.DOUBLE_EPSILON) { bounds.Add(new Vector2(anchor.NextAnchor.Pos.Y)); } else { bounds.Add(new Vector2(anchor.NextAnchor.Pos.Y) + new Vector2(Precision.DOUBLE_EPSILON).PerpendicularRight + new Vector2(ViewModel.VelocityLimit * ViewModel.SvGraphMultiplier * (newVector2.X - anchor.NextAnchor.Pos.X) / maxSpeed) .PerpendicularRight); } } // Clamp the new Y value between all the bounds var newY = bounds.Aggregate(newVector2.Y, (current, bound) => MathHelper.Clamp(current, bound.X, bound.Y)); // Break if the resulting value is not inside all the bounds if (!bounds.All(b => newY >= b.X && newY <= b.Y)) { break; } anchor.SetValue(e.Property, new Vector2(newVector2.X, newY)); } break; } _ignoreAnchorsChange = false; Graph.IgnoreAnchorUpdates = false; } if (ViewModel.PixelLength < HitObjectElement.MaxPixelLength) { AnimateProgress(GraphHitObjectElement); } UpdatePointsOfInterest(); UpdateVelocity(); }
public Section(IAnimeEditor anime, AnimateHandler <T> handler) { this.anime = anime; this.handler = handler; interpolator = InterpolatorHelper.GetInterpolator <T>(); }