public static bool ApproximatelyEquivalent(Vector3 a, Vector3 b, float tolerance) { return (MathHelper.ApproximatelyEquivalent(a.X, b.X, tolerance) && MathHelper.ApproximatelyEquivalent(a.Y, b.Y, tolerance) && MathHelper.ApproximatelyEquivalent(a.Z, b.Z, tolerance)); }
private bool CheckMpB(double mpbNew, List <Marker> markers, TimingPoint redline, Arguments arg) { // For each their beatsFromRedline must stay the same AND their time must be within leniency of their resnapped time // If any of these times becomes incompatible, place a new anchor on the last time and not change the previous redline double mpbOld = redline.MpB; double beatsFromRedline = 0; bool canChangeRedline = true; foreach (Marker markerB in markers) { double timeB = markerB.Time; beatsFromRedline += markerB.BeatsFromLastMarker; // Get the beatsFromRedline after changing mpb redline.MpB = mpbNew; double resnappedTimeBA = redline.Offset + redline.MpB * beatsFromRedline; double beatsFromRedlineBA = (resnappedTimeBA - redline.Offset) / redline.MpB; // Change MpB back so the redline doesn't get changed redline.MpB = mpbOld; // Check changes if (MathHelper.ApproximatelyEquivalent(beatsFromRedlineBA, beatsFromRedline, 0.1) && IsSnapped(timeB, resnappedTimeBA, arg.Leniency)) { continue; } canChangeRedline = false; } return(canChangeRedline); }
/// <summary> /// Get the angle in degrees between two points, clockwise from a to b /// when looking against the direction of the normal. /// </summary> /// <param name="a">The starting point.</param> /// <param name="b">The ending point.</param> /// <param name="normal">The normal of the points' face.</param> /// <returns>The angle between a and b, in degrees.</returns> public static double GetClockwiseAngle(Vector3 a, Vector3 b, Vector3 normal) { double result = Double.NaN; double angle = SignedAngleBetweenVectors(a, b, normal); if (angle > 0.0 || MathHelper.ApproximatelyEquivalent(angle, -180.0, 0.001)) { result = angle; } return(result); }
public static Vector3 Intersect(Plane a, Plane b, Plane c) { float denominator = Vector3.Dot(a.Normal, Vector3.Cross(b.Normal, c.Normal)); // Planes do not intersect. if (MathHelper.ApproximatelyEquivalent(denominator, 0.0f, 0.0001f)) { return(new Vector3(Single.NaN)); } var crossAB = Vector3.Cross(a.Normal, b.Normal); crossAB *= c.DistanceFromOrigin; var crossBC = Vector3.Cross(b.Normal, c.Normal); crossBC *= a.DistanceFromOrigin; var crossCA = Vector3.Cross(c.Normal, a.Normal); crossCA *= b.DistanceFromOrigin; return(new Vector3(crossBC + crossCA + crossAB) / denominator); }
private string Adjust_Timing(Arguments arg, BackgroundWorker worker, DoWorkEventArgs _) { // Count int RedlinesAdded = 0; bool editorRead = EditorReaderStuff.TryGetFullEditorReader(out var reader); foreach (string path in arg.Paths) { // Open beatmap var editor = EditorReaderStuff.GetBeatmapEditor(path, reader, editorRead); Beatmap beatmap = editor.Beatmap; Timing timing = beatmap.BeatmapTiming; // Get all the times to snap List <Marker> markers = new List <Marker>(); if (arg.Objects) { foreach (HitObject ho in beatmap.HitObjects) { markers.Add(new Marker(ho.Time)); } } if (arg.Bookmarks) { foreach (double time in beatmap.GetBookmarks()) { markers.Add(new Marker(time)); } } if (arg.Greenlines) { // Get the offsets of greenlines foreach (TimingPoint tp in timing.TimingPoints) { if (tp.Uninherited == false) { markers.Add(new Marker(tp.Offset)); } } } if (arg.Redlines) { // Get the offsets of redlines foreach (TimingPoint tp in timing.TimingPoints) { if (tp.Uninherited == true) { markers.Add(new Marker(tp.Offset)); } } } // Update progressbar if (worker != null && worker.WorkerReportsProgress) { worker.ReportProgress(20); } // Sort the markers markers = markers.OrderBy(o => o.Time).ToList(); // Calculate the beats between time and the last time or redline for each time // Time the same is 0 // Time a little after is smallest snap for (int i = 0; i < markers.Count; i++) { Marker marker = markers[i]; double time = marker.Time; TimingPoint redline = timing.GetRedlineAtTime(time - 1); // Resnap to that redline only double resnappedTime = timing.Resnap(time, arg.Snap1, arg.Snap2, false, redline); // Calculate beats from the redline double beatsFromRedline = (resnappedTime - redline.Offset) / redline.MpB; // Avoid problems if (MathHelper.ApproximatelyEquivalent(beatsFromRedline, 0, 0.0001)) { beatsFromRedline = 1 / Math.Max(arg.Snap1, arg.Snap2); } if (time == redline.Offset) { beatsFromRedline = 0; } // Initialize the beats from last marker double beatsFromLastMarker = beatsFromRedline; // Get the times between redline and this time List <Marker> timesBefore = markers.Where(o => o.Time <time && o.Time> redline.Offset).ToList(); if (timesBefore.Count > 0) { // Get the last time info double lastTime = timesBefore.Last().Time; double resnappedTimeL = timing.Resnap(lastTime, arg.Snap1, arg.Snap2, false); // Change the beats from last marker beatsFromLastMarker = (resnappedTime - resnappedTimeL) / redline.MpB; // Avoid problems if (MathHelper.ApproximatelyEquivalent(beatsFromLastMarker, 0, 0.0001)) { beatsFromLastMarker = 1f / Math.Max(arg.Snap1, arg.Snap2); } if (lastTime == time) { beatsFromLastMarker = 0; } } // Set the variable marker.BeatsFromLastMarker = beatsFromLastMarker; } // Remove redlines except the first redline if (!arg.Redlines) { var first = timing.TimingPoints.FirstOrDefault(o => o.Uninherited); timing.TimingPoints.RemoveAll(o => o.Uninherited && o != first); } // Update progressbar if (worker != null && worker.WorkerReportsProgress) { worker.ReportProgress(40); } // Loop through all the markers for (int i = 0; i < markers.Count; i++) { Marker marker = markers[i]; double time = marker.Time; TimingPoint redline = timing.GetRedlineAtTime(time - 1); double beatsFromLastMarker = arg.BeatsBetween != -1 ? arg.BeatsBetween : marker.BeatsFromLastMarker; // Skip if 0 beats from last marker if (beatsFromLastMarker == 0) { continue; } // Get the times between redline and this time including this time List <Marker> markersBefore = markers.Where(o => o.Time <time && o.Time> redline.Offset).ToList(); markersBefore.Add(marker); // Calculate MpB // Average MpB from timesBefore and use time from redline double mpb = 0; double beatsFromRedline = 0; foreach (Marker markerB in markersBefore) { beatsFromRedline += markerB.BeatsFromLastMarker; mpb += GetMpB(markerB.Time - redline.Offset, beatsFromRedline, 0); } mpb /= markersBefore.Count; // Check if this MpB doesn't make the markers go offsnap too far bool canChangeRedline = CheckMpB(mpb, markersBefore, redline, arg); // Make changes if (canChangeRedline) { // Round the MpB to human values first mpb = HumanRoundMpB(mpb, markersBefore, redline, arg); // Change the MpB of the redline redline.MpB = mpb; } else { // Get the last time info and not the current markersBefore.Remove(marker); double lastTime = markersBefore.Last().Time; // Make new redline TimingPoint newRedline = redline.Copy(); TimingPoint lastHitsounds = timing.GetTimingPointAtTime(lastTime + 5); newRedline.Offset = lastTime; newRedline.OmitFirstBarLine = arg.OmitBarline; // Set omit to the argument newRedline.Kiai = lastHitsounds.Kiai; newRedline.SampleIndex = lastHitsounds.SampleIndex; newRedline.SampleSet = lastHitsounds.SampleSet; newRedline.Volume = lastHitsounds.Volume; timing.TimingPoints.Add(newRedline); timing.Sort(); // Set the MpB newRedline.MpB = GetMpB(time - lastTime, beatsFromLastMarker, arg.Leniency); // Update the counter RedlinesAdded++; } // Update progressbar if (worker != null && worker.WorkerReportsProgress) { worker.ReportProgress(i * 60 / markers.Count + 40); } } // Save the file editor.SaveFile(); } // Complete progressbar if (worker != null && worker.WorkerReportsProgress) { worker.ReportProgress(100); } // Make an accurate message string message = "Successfully added "; message += RedlinesAdded; if (Math.Abs(RedlinesAdded) == 1) { message += " redlines!"; } else { message += " redlines!"; } return(message); }
private string Adjust_Timing(TimingHelperVm arg, BackgroundWorker worker, DoWorkEventArgs _) { // Count int redlinesAdded = 0; var reader = EditorReaderStuff.GetFullEditorReaderOrNot(); foreach (string path in arg.Paths) { // Open beatmap var editor = EditorReaderStuff.GetNewestVersionOrNot(path, reader); Beatmap beatmap = editor.Beatmap; Timing timing = beatmap.BeatmapTiming; // Get all the times to snap List <Marker> markers = new List <Marker>(); if (arg.Objects) { markers.AddRange(beatmap.HitObjects.Select(ho => new Marker(ho.Time))); } if (arg.Bookmarks) { markers.AddRange(beatmap.GetBookmarks().Select(time => new Marker(time))); } if (arg.Greenlines) { // Get the offsets of greenlines markers.AddRange(from tp in timing.TimingPoints where !tp.Uninherited select new Marker(tp.Offset)); } if (arg.Redlines) { // Get the offsets of redlines markers.AddRange(from tp in timing.TimingPoints where tp.Uninherited select new Marker(tp.Offset)); } // Update progressbar if (worker != null && worker.WorkerReportsProgress) { worker.ReportProgress(20); } // Sort the markers markers = markers.OrderBy(o => o.Time).ToList(); // If there are no redlines add one with a default BPM if (!timing.TimingPoints.Any(tp => tp.Uninherited)) { timing.Add(new TimingPoint(0, 1000, 4, SampleSet.Soft, 0, 100, true, false, false)); } // Remove multiple markers on the same tick var newMarkers = new List <Marker>(markers.Count); newMarkers.AddRange(markers.Where((t, i) => i == 0 || Math.Abs(t.Time - markers[i - 1].Time) >= arg.Leniency + Precision.DOUBLE_EPSILON)); markers = newMarkers; // Calculate the beats between time and the last time or redline for each time // Time the same is 0 // Time a little after is smallest snap foreach (var marker in markers) { double time = marker.Time; TimingPoint redline = timing.GetRedlineAtTime(time - 1); // Resnap to that redline only double resnappedTime = timing.Resnap(time, arg.BeatDivisors, false, tp: redline); // Calculate beats from the redline double beatsFromRedline = (resnappedTime - redline.Offset) / redline.MpB; // Avoid problems if (MathHelper.ApproximatelyEquivalent(beatsFromRedline, 0, 0.0001)) { beatsFromRedline = arg.BeatDivisors.Min(o => o.GetValue()); } if (time == redline.Offset) { beatsFromRedline = 0; } // Initialize the beats from last marker double beatsFromLastMarker = beatsFromRedline; // Get the times between redline and this time List <Marker> timesBefore = markers.Where(o => o.Time <time && o.Time> redline.Offset).ToList(); if (timesBefore.Count > 0) { // Get the last time info double lastTime = timesBefore.Last().Time; double resnappedTimeL = timing.Resnap(lastTime, arg.BeatDivisors, false); // Change the beats from last marker beatsFromLastMarker = (resnappedTime - resnappedTimeL) / redline.MpB; // Avoid problems if (MathHelper.ApproximatelyEquivalent(beatsFromLastMarker, 0, 0.0001)) { beatsFromLastMarker = arg.BeatDivisors.Min(o => o.GetValue()); } if (lastTime == time) { beatsFromLastMarker = 0; } } // Set the variable marker.BeatsFromLastMarker = beatsFromLastMarker; if (arg.BeatsBetween != -1) { marker.BeatsFromLastMarker = arg.BeatsBetween; } } // Remove redlines except the first redline if (!arg.Redlines) { var first = timing.TimingPoints.FirstOrDefault(o => o.Uninherited); timing.RemoveAll(o => o.Uninherited && o != first); } // Update progressbar if (worker != null && worker.WorkerReportsProgress) { worker.ReportProgress(40); } // Loop through all the markers for (int i = 0; i < markers.Count; i++) { Marker marker = markers[i]; double time = marker.Time; TimingPoint redline = timing.GetRedlineAtTime(time - 1); double beatsFromLastMarker = marker.BeatsFromLastMarker; // Skip if 0 beats from last marker if (beatsFromLastMarker == 0) { continue; } // Get the times between redline and this time including this time List <Marker> markersBefore = markers.Where(o => o.Time <time && o.Time> redline.Offset).ToList(); markersBefore.Add(marker); // Calculate MpB // Average MpB from timesBefore and use time from redline double mpb = 0; double beatsFromRedline = 0; foreach (Marker markerB in markersBefore) { beatsFromRedline += markerB.BeatsFromLastMarker; mpb += GetMpB(markerB.Time - redline.Offset, beatsFromRedline, 0); } mpb /= markersBefore.Count; // Check if this MpB doesn't make the markers go offsnap too far bool canChangeRedline = CheckMpB(mpb, markersBefore, redline, arg); // Make changes if (canChangeRedline) { // Round the MpB to human values first mpb = HumanRoundMpB(mpb, markersBefore, redline, arg); // Change the MpB of the redline redline.MpB = mpb; } else { // Get the last time info and not the current markersBefore.Remove(marker); double lastTime = markersBefore.Last().Time; // Make new redline TimingPoint newRedline = redline.Copy(); TimingPoint lastHitsounds = timing.GetTimingPointAtTime(lastTime + 5); newRedline.Offset = lastTime; newRedline.OmitFirstBarLine = arg.OmitBarline; // Set omit to the argument newRedline.Kiai = lastHitsounds.Kiai; newRedline.SampleIndex = lastHitsounds.SampleIndex; newRedline.SampleSet = lastHitsounds.SampleSet; newRedline.Volume = lastHitsounds.Volume; timing.Add(newRedline); // Set the MpB newRedline.MpB = GetMpB(time - lastTime, beatsFromLastMarker, arg.Leniency); // Update the counter redlinesAdded++; } // Update progressbar if (worker != null && worker.WorkerReportsProgress) { worker.ReportProgress(i * 60 / markers.Count + 40); } } // Save the file editor.SaveFile(); } // Complete progressbar if (worker != null && worker.WorkerReportsProgress) { worker.ReportProgress(100); } // Do QuickRun stuff RunFinished?.Invoke(this, new RunToolCompletedEventArgs(true, reader != null, arg.Quick)); // Make an accurate message string message = "Successfully added "; message += redlinesAdded; if (Math.Abs(redlinesAdded) == 1) { message += " redlines!"; } else { message += " redlines!"; } return(arg.Quick ? string.Empty : message); }
/// <summary> /// Approximates equivalence between two double-precision floating-point numbers on a direct human scale. /// It is important to note that this does not approximate equality - instead, it merely checks whether or not /// two numbers could be considered equivalent to each other within a certain tolerance. The tolerance is /// inclusive. /// </summary> /// <param name="a">The first value to compare.</param> /// <param name="b">The second value to compare.</param> /// <param name="tolerance">The tolerance within which the two values would be considered equivalent.</param> /// <returns>Whether or not the values can be considered equivalent within the tolerance.</returns> public static bool ApproximatelyEquivalent(double a, double b, double tolerance) => MathHelper.ApproximatelyEquivalent(a, b, tolerance);
/// <summary> /// Approximates equivalence between two single-precision floating-point numbers on a direct human scale. /// It is important to note that this does not approximate equality - instead, it merely checks whether or not /// two numbers could be considered equivalent to each other within a certain tolerance. The tolerance is /// inclusive. /// </summary> /// <param name="a">The first value to compare.</param> /// <param name="b">The second value to compare.</param> /// <param name="tolerance">The tolerance within which the two values would be considered equivalent.</param> /// <returns>Whether or not the values can be considered equivalent within the tolerance.</returns> public static bool ApproximatelyEquivalent(float a, float b, float tolerance) => MathHelper.ApproximatelyEquivalent(a, b, tolerance);