private string Complete_Sliders(SliderCompletionatorVm arg, BackgroundWorker worker, DoWorkEventArgs _) { int slidersCompleted = 0; var reader = EditorReaderStuff.GetFullEditorReaderOrNot(out var editorReaderException1); if (arg.ImportModeSetting == SliderCompletionatorVm.ImportMode.Selected && editorReaderException1 != null) { throw new Exception("Could not fetch selected hit objects.", editorReaderException1); } foreach (string path in arg.Paths) { var editor = EditorReaderStuff.GetNewestVersionOrNot(path, reader, out var selected, out var editorReaderException2); if (arg.ImportModeSetting == SliderCompletionatorVm.ImportMode.Selected && editorReaderException2 != null) { throw new Exception("Could not fetch selected hit objects.", editorReaderException2); } Beatmap beatmap = editor.Beatmap; Timing timing = beatmap.BeatmapTiming; List <HitObject> markedObjects = arg.ImportModeSetting == SliderCompletionatorVm.ImportMode.Selected ? selected : arg.ImportModeSetting == SliderCompletionatorVm.ImportMode.Bookmarked ? beatmap.GetBookmarkedObjects() : arg.ImportModeSetting == SliderCompletionatorVm.ImportMode.Time ? beatmap.QueryTimeCode(arg.TimeCode).ToList() : beatmap.HitObjects; for (int i = 0; i < markedObjects.Count; i++) { HitObject ho = markedObjects[i]; if (ho.IsSlider) { double oldSpatialLength = ho.PixelLength; double newSpatialLength = arg.SpatialLength != -1 ? ho.GetSliderPath(fullLength: true).Distance *arg.SpatialLength : oldSpatialLength; double oldTemporalLength = timing.CalculateSliderTemporalLength(ho.Time, ho.PixelLength); double newTemporalLength = arg.TemporalLength != -1 ? timing.GetMpBAtTime(ho.Time) * arg.TemporalLength : oldTemporalLength; double oldSv = timing.GetSvAtTime(ho.Time); double newSv = oldSv / ((newSpatialLength / oldSpatialLength) / (newTemporalLength / oldTemporalLength)); if (double.IsNaN(newSv)) { throw new Exception("Encountered NaN slider velocity. Make sure none of the inputs are zero."); } ho.SliderVelocity = newSv; ho.PixelLength = newSpatialLength; // Scale anchors to completion if (arg.MoveAnchors) { ho.SetAllCurvePoints(SliderPathUtil.MoveAnchorsToLength( ho.GetAllCurvePoints(), ho.SliderType, ho.PixelLength, out var pathType)); ho.SliderType = pathType; } slidersCompleted++; } if (worker != null && worker.WorkerReportsProgress) { worker.ReportProgress(i / markedObjects.Count); } } // Reconstruct SliderVelocity List <TimingPointsChange> timingPointsChanges = new List <TimingPointsChange>(); // Add Hitobject stuff foreach (HitObject ho in beatmap.HitObjects) { if (ho.IsSlider) // SliderVelocity changes { TimingPoint tp = ho.TimingPoint.Copy(); tp.Offset = ho.Time; tp.MpB = ho.SliderVelocity; timingPointsChanges.Add(new TimingPointsChange(tp, mpb: true)); } } // Add the new SliderVelocity changes TimingPointsChange.ApplyChanges(timing, timingPointsChanges); // Save the file editor.SaveFile(); } // Complete progressbar if (worker != null && worker.WorkerReportsProgress) { worker.ReportProgress(100); } // Do stuff if (arg.Quick) { RunFinished?.Invoke(this, new RunToolCompletedEventArgs(true, reader != null)); } // Make an accurate message string message = ""; if (Math.Abs(slidersCompleted) == 1) { message += "Successfully completed " + slidersCompleted + " slider!"; } else { message += "Successfully completed " + slidersCompleted + " sliders!"; } return(arg.Quick ? "" : message); }
private string Complete_Sliders(Arguments arg, BackgroundWorker worker, DoWorkEventArgs _) { int slidersCompleted = 0; bool editorRead = EditorReaderStuff.TryGetFullEditorReader(out var reader); foreach (string path in arg.Paths) { var editor = EditorReaderStuff.GetBeatmapEditor(path, reader, editorRead, out var selected, out var editorActuallyRead); if (arg.SelectionMode == 0 && !editorActuallyRead) { return(EditorReaderStuff.SelectedObjectsReadFailText); } Beatmap beatmap = editor.Beatmap; Timing timing = beatmap.BeatmapTiming; List <HitObject> markedObjects = arg.SelectionMode == 0 ? selected : arg.SelectionMode == 1 ? beatmap.GetBookmarkedObjects() : beatmap.HitObjects; for (int i = 0; i < markedObjects.Count; i++) { HitObject ho = markedObjects[i]; if (ho.IsSlider) { double oldSpatialLength = ho.PixelLength; double newSpatialLength = arg.SpatialLength != -1 ? ho.GetSliderPath(fullLength: true).Distance *arg.SpatialLength : oldSpatialLength; double oldTemporalLength = timing.CalculateSliderTemporalLength(ho.Time, ho.PixelLength); double newTemporalLength = arg.TemporalLength != -1 ? timing.GetMpBAtTime(ho.Time) * arg.TemporalLength : oldTemporalLength; double oldSv = timing.GetSvAtTime(ho.Time); double newSv = oldSv / ((newSpatialLength / oldSpatialLength) / (newTemporalLength / oldTemporalLength)); ho.SliderVelocity = newSv; ho.PixelLength = newSpatialLength; // Scale anchors to completion if (arg.MoveAnchors) { ho.SetAllCurvePoints(SliderPathUtil.MoveAnchorsToLength( ho.GetAllCurvePoints(), ho.SliderType, ho.PixelLength, out var pathType)); ho.SliderType = pathType; } slidersCompleted++; } if (worker != null && worker.WorkerReportsProgress) { worker.ReportProgress(i / markedObjects.Count); } } // Reconstruct SliderVelocity List <TimingPointsChange> timingPointsChanges = new List <TimingPointsChange>(); // Add Hitobject stuff foreach (HitObject ho in beatmap.HitObjects) { if (ho.IsSlider) // SliderVelocity changes { TimingPoint tp = ho.TimingPoint.Copy(); tp.Offset = ho.Time; tp.MpB = ho.SliderVelocity; timingPointsChanges.Add(new TimingPointsChange(tp, mpb: true)); } } // Add the new SliderVelocity changes TimingPointsChange.ApplyChanges(timing, timingPointsChanges); // Save the file editor.SaveFile(); } // Complete progressbar if (worker != null && worker.WorkerReportsProgress) { worker.ReportProgress(100); } // Do stuff if (arg.Quick) { RunFinished?.Invoke(this, new RunToolCompletedEventArgs(true, editorRead)); } // Make an accurate message string message = ""; if (Math.Abs(slidersCompleted) == 1) { message += "Successfully completed " + slidersCompleted + " slider!"; } else { message += "Successfully completed " + slidersCompleted + " sliders!"; } return(arg.Quick ? "" : message); }
private string Complete_Sliders(SliderCompletionatorVm arg, BackgroundWorker worker, DoWorkEventArgs _) { int slidersCompleted = 0; var reader = EditorReaderStuff.GetFullEditorReaderOrNot(out var editorReaderException1); if (arg.ImportModeSetting == SliderCompletionatorVm.ImportMode.Selected && editorReaderException1 != null) { throw new Exception("Could not fetch selected hit objects.", editorReaderException1); } foreach (string path in arg.Paths) { var editor = EditorReaderStuff.GetNewestVersionOrNot(path, reader, out var selected, out var editorReaderException2); if (arg.ImportModeSetting == SliderCompletionatorVm.ImportMode.Selected && editorReaderException2 != null) { throw new Exception("Could not fetch selected hit objects.", editorReaderException2); } Beatmap beatmap = editor.Beatmap; Timing timing = beatmap.BeatmapTiming; List <HitObject> markedObjects = arg.ImportModeSetting switch { SliderCompletionatorVm.ImportMode.Selected => selected, SliderCompletionatorVm.ImportMode.Bookmarked => beatmap.GetBookmarkedObjects(), SliderCompletionatorVm.ImportMode.Time => beatmap.QueryTimeCode(arg.TimeCode).ToList(), SliderCompletionatorVm.ImportMode.Everything => beatmap.HitObjects, _ => throw new ArgumentException("Unexpected import mode.") }; for (int i = 0; i < markedObjects.Count; i++) { HitObject ho = markedObjects[i]; if (ho.IsSlider) { double mpb = timing.GetMpBAtTime(ho.Time); double oldDuration = timing.CalculateSliderTemporalLength(ho.Time, ho.PixelLength); double oldLength = ho.PixelLength; double oldSv = timing.GetSvAtTime(ho.Time); double newDuration = arg.UseEndTime ? arg.EndTime == -1 ? oldDuration : arg.EndTime - ho.Time : arg.Duration == -1 ? oldDuration : timing.WalkBeatsInMillisecondTime(arg.Duration, ho.Time) - ho.Time; double newLength = arg.Length == -1 ? oldLength : ho.GetSliderPath(fullLength: true).Distance *arg.Length; double newSv = arg.SliderVelocity == -1 ? oldSv : -100 / arg.SliderVelocity; switch (arg.FreeVariableSetting) { case SliderCompletionatorVm.FreeVariable.Velocity: newSv = -10000 * timing.SliderMultiplier * newDuration / (newLength * mpb); break; case SliderCompletionatorVm.FreeVariable.Duration: // This actually doesn't get used anymore because the .osu doesn't store the duration newDuration = newLength * newSv * mpb / (-10000 * timing.SliderMultiplier); break; case SliderCompletionatorVm.FreeVariable.Length: newLength = -10000 * timing.SliderMultiplier * newDuration / (newSv * mpb); break; default: throw new ArgumentException("Unexpected free variable setting."); } if (double.IsNaN(newSv)) { throw new Exception("Encountered NaN slider velocity. Make sure none of the inputs are zero."); } if (newDuration < 0) { throw new Exception("Encountered slider with negative duration. Make sure the end time is greater than the end time of all selected sliders."); } ho.SliderVelocity = newSv; ho.PixelLength = newLength; // Scale anchors to completion if (arg.MoveAnchors) { ho.SetAllCurvePoints(SliderPathUtil.MoveAnchorsToLength( ho.GetAllCurvePoints(), ho.SliderType, ho.PixelLength, out var pathType)); ho.SliderType = pathType; } slidersCompleted++; } if (worker != null && worker.WorkerReportsProgress) { worker.ReportProgress(i / markedObjects.Count); } } // Reconstruct SliderVelocity List <TimingPointsChange> timingPointsChanges = new List <TimingPointsChange>(); // Add Hitobject stuff foreach (HitObject ho in beatmap.HitObjects) { // SliderVelocity changes if (ho.IsSlider) { if (markedObjects.Contains(ho) && arg.DelegateToBpm) { var tpAfter = timing.GetRedlineAtTime(ho.Time).Copy(); var tpOn = tpAfter.Copy(); tpAfter.Offset = ho.Time; tpOn.Offset = ho.Time - 1; // This one will be on the slider tpAfter.OmitFirstBarLine = true; tpOn.OmitFirstBarLine = true; // Express velocity in BPM tpOn.MpB *= ho.SliderVelocity / -100; // NaN SV results in removal of slider ticks ho.SliderVelocity = arg.RemoveSliderTicks ? double.NaN : -100; // Add redlines timingPointsChanges.Add(new TimingPointsChange(tpOn, mpb: true, unInherited: true, omitFirstBarLine: true, fuzzyness: Precision.DOUBLE_EPSILON)); timingPointsChanges.Add(new TimingPointsChange(tpAfter, mpb: true, unInherited: true, omitFirstBarLine: true, fuzzyness: Precision.DOUBLE_EPSILON)); ho.Time -= 1; } TimingPoint tp = ho.TimingPoint.Copy(); tp.Offset = ho.Time; tp.MpB = ho.SliderVelocity; timingPointsChanges.Add(new TimingPointsChange(tp, mpb: true, fuzzyness: Precision.DOUBLE_EPSILON)); } } // Add the new SliderVelocity changes TimingPointsChange.ApplyChanges(timing, timingPointsChanges); // Save the file editor.SaveFile(); } // Complete progressbar if (worker != null && worker.WorkerReportsProgress) { worker.ReportProgress(100); } // Do stuff RunFinished?.Invoke(this, new RunToolCompletedEventArgs(true, reader != null, arg.Quick)); // Make an accurate message string message = ""; if (Math.Abs(slidersCompleted) == 1) { message += "Successfully completed " + slidersCompleted + " slider!"; } else { message += "Successfully completed " + slidersCompleted + " sliders!"; } return(arg.Quick ? "" : message); }
private string Merge_Sliders(SliderMergerVm arg, BackgroundWorker worker) { var slidersMerged = 0; var reader = EditorReaderStuff.GetFullEditorReaderOrNot(out var editorReaderException1); if (arg.ImportModeSetting == 0 && editorReaderException1 != null) { throw new Exception("Could not fetch selected hit objects.", editorReaderException1); } foreach (var path in arg.Paths) { var editor = EditorReaderStuff.GetNewestVersionOrNot(path, reader, out var selected, out var editorReaderException2); if (arg.ImportModeSetting == SliderMergerVm.ImportMode.Selected && editorReaderException2 != null) { throw new Exception("Could not fetch selected hit objects.", editorReaderException2); } var beatmap = editor.Beatmap; var markedObjects = arg.ImportModeSetting == 0 ? selected : arg.ImportModeSetting == SliderMergerVm.ImportMode.Bookmarked ? beatmap.GetBookmarkedObjects() : arg.ImportModeSetting == SliderMergerVm.ImportMode.Time ? beatmap.QueryTimeCode(arg.TimeCode).ToList() : beatmap.HitObjects; var mergeLast = false; for (var i = 0; i < markedObjects.Count - 1; i++) { if (worker != null && worker.WorkerReportsProgress) { worker.ReportProgress(i / markedObjects.Count); } var ho1 = markedObjects[i]; var ho2 = markedObjects[i + 1]; var lastPos1 = ho1.IsSlider ? arg.MergeOnSliderEnd ? ho1.GetSliderPath().PositionAt(1) : ho1.CurvePoints.Last() : ho1.Pos; double dist = Vector2.Distance(lastPos1, ho2.Pos); if (dist > arg.Leniency) { mergeLast = false; continue; } if (ho1.IsSlider && ho2.IsSlider) { if (arg.MergeOnSliderEnd) { // In order to merge on the slider end we first move the anchors such that the last anchor is exactly on the slider end // After that merge as usual ho1.SetAllCurvePoints(SliderPathUtil.MoveAnchorsToLength( ho1.GetAllCurvePoints(), ho1.SliderType, ho1.PixelLength, out var pathType)); ho1.SliderType = pathType; } var sp1 = BezierConverter.ConvertToBezier(ho1.SliderPath).ControlPoints; var sp2 = BezierConverter.ConvertToBezier(ho2.SliderPath).ControlPoints; double extraLength = 0; switch (arg.ConnectionModeSetting) { case SliderMergerVm.ConnectionMode.Move: Move(sp2, sp1.Last() - sp2.First()); break; case SliderMergerVm.ConnectionMode.Linear: sp1.Add(sp1.Last()); sp1.Add(sp2.First()); extraLength = (ho1.CurvePoints.Last() - ho2.Pos).Length; break; } var mergedAnchors = sp1.Concat(sp2).ToList(); mergedAnchors.Round(); var linearLinear = arg.LinearOnLinear && IsLinearBezier(sp1) && IsLinearBezier(sp2); if (linearLinear) { for (var j = 0; j < mergedAnchors.Count - 1; j++) { if (mergedAnchors[j] != mergedAnchors[j + 1]) { continue; } mergedAnchors.RemoveAt(j); j--; } } var mergedPath = new SliderPath(linearLinear ? PathType.Linear : PathType.Bezier, mergedAnchors.ToArray(), ho1.PixelLength + ho2.PixelLength + extraLength); ho1.SliderPath = mergedPath; beatmap.HitObjects.Remove(ho2); markedObjects.Remove(ho2); i--; slidersMerged++; if (!mergeLast) { slidersMerged++; } mergeLast = true; } else if (ho1.IsSlider && ho2.IsCircle) { var sp1 = BezierConverter.ConvertToBezier(ho1.SliderPath).ControlPoints; sp1.Add(sp1.Last()); sp1.Add(ho2.Pos); var extraLength = (ho1.CurvePoints.Last() - ho2.Pos).Length; var mergedAnchors = sp1; mergedAnchors.Round(); var linearLinear = arg.LinearOnLinear && IsLinearBezier(sp1); if (linearLinear) { for (var j = 0; j < mergedAnchors.Count - 1; j++) { if (mergedAnchors[j] != mergedAnchors[j + 1]) { continue; } mergedAnchors.RemoveAt(j); j--; } } var mergedPath = new SliderPath(linearLinear ? PathType.Linear : PathType.Bezier, mergedAnchors.ToArray(), ho1.PixelLength + extraLength); ho1.SliderPath = mergedPath; beatmap.HitObjects.Remove(ho2); markedObjects.Remove(ho2); i--; slidersMerged++; if (!mergeLast) { slidersMerged++; } mergeLast = true; } else if (ho1.IsCircle && ho2.IsSlider) { var sp2 = BezierConverter.ConvertToBezier(ho2.SliderPath).ControlPoints; sp2.Insert(0, sp2.First()); sp2.Insert(0, ho1.Pos); var extraLength = (ho1.Pos - ho2.Pos).Length; var mergedAnchors = sp2; mergedAnchors.Round(); var linearLinear = arg.LinearOnLinear && IsLinearBezier(sp2); if (linearLinear) { for (var j = 0; j < mergedAnchors.Count - 1; j++) { if (mergedAnchors[j] != mergedAnchors[j + 1]) { continue; } mergedAnchors.RemoveAt(j); j--; } } var mergedPath = new SliderPath(linearLinear ? PathType.Linear : PathType.Bezier, mergedAnchors.ToArray(), ho2.PixelLength + extraLength); ho2.SliderPath = mergedPath; beatmap.HitObjects.Remove(ho1); markedObjects.Remove(ho1); i--; slidersMerged++; if (!mergeLast) { slidersMerged++; } mergeLast = true; } else if (ho1.IsCircle && ho2.IsCircle) { var mergedAnchors = new List <Vector2> { ho1.Pos, ho2.Pos }; var mergedPath = new SliderPath(arg.LinearOnLinear ? PathType.Linear : PathType.Bezier, mergedAnchors.ToArray(), (ho1.Pos - ho2.Pos).Length); ho1.SliderPath = mergedPath; ho1.IsCircle = false; ho1.IsSlider = true; ho1.Repeat = 1; ho1.EdgeHitsounds = new List <int> { ho1.GetHitsounds(), ho2.GetHitsounds() }; ho1.EdgeSampleSets = new List <SampleSet> { ho1.SampleSet, ho2.SampleSet }; ho1.EdgeAdditionSets = new List <SampleSet> { ho1.AdditionSet, ho2.AdditionSet }; beatmap.HitObjects.Remove(ho2); markedObjects.Remove(ho2); i--; slidersMerged++; if (!mergeLast) { slidersMerged++; } mergeLast = true; } else { mergeLast = false; } } // Save the file editor.SaveFile(); } // Complete progressbar if (worker != null && worker.WorkerReportsProgress) { worker.ReportProgress(100); } // Do stuff if (arg.Quick) { RunFinished?.Invoke(this, new RunToolCompletedEventArgs(true, reader != null)); } // Make an accurate message var message = ""; if (Math.Abs(slidersMerged) == 1) { message += "Successfully merged " + slidersMerged + " slider!"; } else { message += "Successfully merged " + slidersMerged + " sliders!"; } return(arg.Quick ? "" : message); }