private string Merge_Sliders(Arguments arg, BackgroundWorker worker) { var slidersMerged = 0; var editorRead = EditorReaderStuff.TryGetFullEditorReader(out var reader); foreach (var 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); } var beatmap = editor.Beatmap; var markedObjects = arg.SelectionMode == 0 ? selected : arg.SelectionMode == 1 ? beatmap.GetBookmarkedObjects() : beatmap.HitObjects; var mergeLast = false; for (var i = 0; i < markedObjects.Count - 1; i++) { var ho1 = markedObjects[i]; var ho2 = markedObjects[i + 1]; if (ho1.IsSlider && ho2.IsSlider && (ho1.CurvePoints.Last() - ho2.Pos).Length <= arg.Leniency) { var sp1 = BezierConverter.ConvertToBezier(ho1.SliderPath).ControlPoints; var sp2 = BezierConverter.ConvertToBezier(ho2.SliderPath).ControlPoints; double extraLength = 0; switch (arg.ConnectionMode) { case ConnectionMode.Move: Move(sp2, sp1.Last() - sp2.First()); break; case 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 && (ho1.CurvePoints.Last() - ho2.Pos).Length <= arg.Leniency) { 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 && (ho1.Pos - ho2.Pos).Length <= arg.Leniency) { 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 && (ho1.Pos - ho2.Pos).Length <= arg.Leniency) { 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; } if (worker != null && worker.WorkerReportsProgress) { worker.ReportProgress(i / markedObjects.Count); } } // 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 var message = ""; if (Math.Abs(slidersMerged) == 1) { message += "Successfully merged " + slidersMerged + " slider!"; } else { message += "Successfully merged " + slidersMerged + " sliders!"; } return(arg.Quick ? "" : message); }
public static List <Vector2> MoveAnchorsToLength(List <Vector2> anchors, PathType pathType, double newLength, out PathType newPathType) { var newAnchors = new List <Vector2>(); var sliderPath = new SliderPath(pathType, anchors.ToArray(), newLength); var maxSliderPath = new SliderPath(pathType, anchors.ToArray()); if (newLength > maxSliderPath.Distance) { // Extend linearly switch (pathType) { case PathType.Bezier: newPathType = PathType.Bezier; newAnchors.AddRange(anchors); newAnchors.Add(anchors.Last()); newAnchors.Add(sliderPath.PositionAt(1)); break; case PathType.Catmull: case PathType.PerfectCurve: // Convert to bezier and then extend newPathType = PathType.Bezier; newAnchors = BezierConverter.ConvertToBezier(sliderPath).ControlPoints; newAnchors.Add(anchors.Last()); newAnchors.Add(sliderPath.PositionAt(1)); break; default: newPathType = pathType; newAnchors.AddRange(anchors); newAnchors[newAnchors.Count - 1] = sliderPath.PositionAt(1); break; } } else { switch (sliderPath.Type) { case PathType.Catmull: case PathType.Bezier: newPathType = PathType.Bezier; // Convert in case the path type is catmull var convert = BezierConverter.ConvertToBezier(sliderPath).ControlPoints; // Find the last bezier segment and the pixel length at that part BezierSubdivision subdivision = null; double totalLength = 0; foreach (var bezierSubdivision in ChopAnchors(convert)) { subdivision = bezierSubdivision; var length = bezierSubdivision.SubdividedApproximationLength(); if (totalLength + length > newLength) { break; } totalLength += length; newAnchors.AddRange(bezierSubdivision.Points); } if (subdivision == null) { break; } // Find T for the remaining pixel length var t = subdivision.LengthToT(newLength - totalLength); // ScaleRight the BezierSubdivision so the anchors end at T subdivision.ScaleRight(t); // Add the scaled anchors newAnchors.AddRange(subdivision.Points); break; case PathType.PerfectCurve: newPathType = PathType.PerfectCurve; newAnchors.AddRange(anchors); newAnchors[1] = sliderPath.PositionAt(0.5); newAnchors[2] = sliderPath.PositionAt(1); break; default: newPathType = pathType; if (anchors.Count > 2) { // Find the section of the linear slider which contains the slider end totalLength = 0; foreach (var bezierSubdivision in ChopAnchorsLinear(anchors)) { newAnchors.Add(bezierSubdivision.Points[0]); var length = bezierSubdivision.Length(); if (totalLength + length > newLength) { break; } totalLength += length; } newAnchors.Add(sliderPath.PositionAt(1)); } else { newAnchors.AddRange(anchors); newAnchors[newAnchors.Count - 1] = sliderPath.PositionAt(1); } break; } } return(newAnchors); }
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); }