//Get a string array from an entire section public string[] GetSection(string sectionname) { //Make a list of tags that will be returned List <string> tags = new List <string>(); //Get where the section begins int sectionline = this.section.GetSectionLine(sectionname); //Loop through each line of the section //Start at sectionline + 1 to skip the section header itself for (int i = sectionline + 1; i < filelines.Length; i++) { //Exit the loop if we hit another section if (section.IsSection(filelines[i])) { break; } //Otherwise add the line to the list tags.Add(filelines[i]); } string[] finaltags = tags.ToArray(); finaltags = Dewlib.RemoveEmptyEntries(finaltags); //Return the list return(finaltags); }
private double Accuracy() { if (TotalHits() == 0) { return(0); } return(Dewlib.Clamp((double)TotalSuccessfulHits() / TotalHits(), 0.0, 1.0)); }
//TODO: Throw error if somethings messed up with the timing section //Calculates the slider velocity at a specified time using the default //velocity and the relevant timing section //Timing points inside sliders don't affect the slider itself protected double GetSliderVelocity() { int ms = Int32.Parse(HitObjectParser.GetProperty(id, "time")); //Get the default slider velocity of the beatmap double slidervelocity = Double.Parse(map.GetTag("Difficulty", "SliderMultiplier"), CultureInfo.InvariantCulture); //Get all the timing sections of the beatmap string[] timings = this.map.GetSection("TimingPoints"); //Will hold the relevant timing point string timingpoint = null; //Find the section that applies to the given time for (int i = 0; i < timings.Length; i++) { //Split the string by commas to get all the relevant times string[] attributes = timings[i].Split(','); //Trim each string just in case attributes = Dewlib.TrimStringArray(attributes); //If the timing point is a higher time, then we want the previous timing section if (Int32.Parse(attributes[0]) > ms) { //avoid accessing a negative timing point if (i == 0) { timingpoint = timings[0]; } else { timingpoint = timings[i - 1]; } break; } } //If the timing point needed is the very last one if (timingpoint == null) { timingpoint = timings[timings.Length - 1]; } string[] properties = timingpoint.Split(','); //If the offset is positive, then there is no slider multiplication if (Double.Parse(properties[1], CultureInfo.InvariantCulture) > 0) { return(slidervelocity); } //Otherwise the slider multiplier is 100 / abs(offset) else { double offset = Double.Parse(properties[1], CultureInfo.InvariantCulture); return(slidervelocity * (100 / Math.Abs(offset))); } }
//Gets the difficulty of the entire map public double GetDifficulty() { HitPoint[] notes = this.GetNoteDifficulty(); List <double> notedifficulties = new List <double>(); foreach (HitPoint notepoint in notes) { notedifficulties.Add(notepoint.HitDifficulty); } return(Dewlib.SumScaledList(notedifficulties.ToArray(), 0.95)); }
//Calculates the average of the top ten % of speeds between hitpoints in the given beatmap //(speeds = change in position / change in time) public double CalculateDistances() { List <double> speeds = new List <double>(); for (int i = 1; i < positions.Length; i++) { //Cast to make division operation a double speeds.Add(Math.Abs(positions[i] - positions[i - 1]) / (double)(times[i] - times[i - 1])); } return(Dewlib.SumScaledList(speeds.ToArray(), 0.95)); }
public Point[] GetTickLocations(double interval, int count, int length) { List <Point> ticks = new List <Point>(); //Make the number of steps 1000 for each curve double steps = 1000 / controlpoints.Count - 2 - 1; //how much to increment t by with every loop double increment = 1 / steps; //how much along the curve we have traveled so far double travelled = 0; //where to get the next point on a given curve //assign increment to get the next intended point double t = increment; //track which curve (defined by two points) is being looked at //start at 1 to not break tangent points int curvestartpoint = 1; Point prev = controlpoints[0]; //Subtract two for the extra points to get the number of curves while (curvestartpoint < controlpoints.Count - 2) { Point next = GetPointBetween(curvestartpoint, curvestartpoint + 1, t); double distance = Dewlib.GetDistance(prev.x, prev.y, next.x, next.y); travelled += distance; prev = next; if (travelled >= interval) { ticks.Add(next); travelled = 0; if (ticks.Count == count) { break; } } t += increment; if (t > 1) { curvestartpoint++; t -= 1; } } if (travelled > 0) { throw new Exception("Error, too many ticks to get in catmull curve"); } return(ticks.ToArray()); }
//Helper method that calculates the angles of the first and last points //of the curve private void calculateAngles(Point p1, Point p2, Point p3, double length) { this.startangle = Dewlib.RestrictRange(Math.Atan2(p1.y - center.y, p1.x - center.x), 0, 2 * Math.PI); double midangle = Dewlib.RestrictRange(Math.Atan2(p2.y - center.y, p2.x - center.x), 0, 2 * Math.PI); //NOT the last point of this curve //Only used to calculate the direction of the curve double lastangle = Dewlib.RestrictRange(Math.Atan2(p3.y - center.y, p3.x - center.x), 0, 2 * Math.PI); if (startangle >= midangle && midangle >= lastangle) { clockwise = false; } else if (startangle >= lastangle && lastangle >= midangle) { clockwise = true; } else if (midangle >= startangle && startangle >= lastangle) { clockwise = true; } else if (midangle >= lastangle && lastangle >= startangle) { clockwise = false; } else if (lastangle >= startangle && startangle >= midangle) { clockwise = false; } else if (lastangle >= midangle && midangle >= startangle) { clockwise = true; } //Use the arclength to calculate the final angle since the last control point //of the slider is NOT the last point of the curve //This is an angle differential since the formula assumes a start from an angle of 0 double anglediff = length / radius; if (clockwise) { this.endangle = Dewlib.RestrictRange(startangle + anglediff, 0, 2 * Math.PI); } else { this.endangle = Dewlib.RestrictRange(startangle - anglediff, 0, 2 * Math.PI); } }
//Calculates a point on the curve public Point Bezier(double t) { Point result = new Point(0, 0); //Degree of the bezier curve int degree = points.Length - 1; int[] pascalrow = Dewlib.GetPascalRow(degree); for (int i = 0; i < points.Length; i++) { result.x += pascalrow[i] * Math.Pow((1 - t), degree - i) * Math.Pow(t, i) * points[i].x; result.y += pascalrow[i] * Math.Pow((1 - t), degree - i) * Math.Pow(t, i) * points[i].y; } return(result); }
//Uses the given list of control points to construct a list of curves //to account for red points public LinearSlider(string id, Beatmap amap) : base(id, amap) { //Get the initial hit point of the slider //Split into three lines for readibility Point initialcoord = new Point(); initialcoord.x = Int32.Parse(HitObjectParser.GetProperty(id, "x")); initialcoord.y = Int32.Parse(HitObjectParser.GetProperty(id, "y")); //List<Point> curvepoints = new List<Point>(); List <LinearCurve> accumulatedcurves = new List <LinearCurve>(); //Normal linear slider if (controlpoints.Length == 1) { accumulatedcurves.Add(new LinearCurve(initialcoord, controlpoints[0])); } else { List <Point> allcontrolpoints = new List <Point>(); //Add first point only if it's not repeated in the control points (old maps) if (initialcoord.IntX() != controlpoints[0].IntX() && initialcoord.IntY() != controlpoints[0].IntY()) { allcontrolpoints.Add(initialcoord); } allcontrolpoints.AddRange(controlpoints); Point[][] curvepoints = Dewlib.SplitPointList(allcontrolpoints.ToArray()); foreach (Point[] curve in curvepoints) { if (curve.Length > 2) { for (int i = 1; i < curve.Length; i++) { accumulatedcurves.Add(new LinearCurve(curve[i - 1], curve[i])); } } else { accumulatedcurves.Add(new LinearCurve(curve[0], curve[1])); } } } curves = accumulatedcurves.ToArray(); }
protected override Point GetLastPoint() { //Necessary to avoid cases where the pixellength is something like 105.000004005432 int length = Convert.ToInt32(Math.Floor(Double.Parse(HitObjectParser.GetProperty(id, "pixelLength"), CultureInfo.InvariantCulture))); //how many steps to travel through the curve //divide by curves.Length to scale this with the number of curves double steps = length * 2 / curves.Length; //how much to increment t by with every loop double increment = 1 / steps; //how much along the curve we have traveled so far double travelled = 0; //where to get the next point on a given curve //assign increment to get the next intended point double t = increment; Point prev = new Point(); prev.x = Int32.Parse(HitObjectParser.GetProperty(id, "x")); prev.y = Int32.Parse(HitObjectParser.GetProperty(id, "y")); //which curve we are looking at int curvenumber = 0; while (curvenumber < curves.Length) { Point next = curves[curvenumber].Bezier(t); double distance = Dewlib.GetDistance(prev.x, prev.y, next.x, next.y); travelled += distance; prev = next; if (travelled >= length) { return(next); } t += increment; if (t > 1) { curvenumber++; t -= 1; } } //If we reached the end of the slider without accumulated sliderlength distance, //just assume that the last point is the last point of the bezier curve return(curves[curves.Length - 1].Bezier(1)); }
public void WriteDebug(DiffCalc[] songs) { if (printdebug) { Console.WriteLine("Writing Debug..."); int donecount = 0; Console.Write("0%\r"); Directory.CreateDirectory("debug"); foreach (DiffCalc calc in songs) { HitPoint[] notes = calc.GetNoteDifficulty(); List <HitPoint> sortednotes = new List <HitPoint>(notes); if (issortdifficulty) { sortednotes.Sort(HitPoint.CompareDifficulty); } else { sortednotes.Sort(HitPoint.CompareTime); } //Make sure files don't have invalid characters in the name string filepath = Dewlib.MakeValidFilename(calc.GetBeatmapTitle() + ".txt"); filepath = "debug//" + filepath; StreamWriter debugfile = new StreamWriter(filepath); //Write the header debugfile.WriteLine("[Difficulty]".PadRight(21) + "[Time]".PadRight(9) + "[Index]"); foreach (HitPoint notepoint in sortednotes) { string difficulty = notepoint.HitDifficulty.ToString(); string time = notepoint.HitTime.ToString(); debugfile.WriteLine(difficulty.PadRight(21) + time.PadRight(9) + notepoint.HitID); } debugfile.Close(); donecount++; Console.Write(Math.Round((double)donecount * 100 / songs.Length) + "%\r"); } } }
//Gets a point on the approximated curve on the circle //Goes from 0 to 1, where 0 is the starting point and 1 is the ending point public Point GetPoint(double t) { //TODO: Make exception more useful if (t < 0 || t > 1) { throw new ArgumentOutOfRangeException(); } double angle; double anglediff; if (clockwise) { if (endangle - startangle > 0) { anglediff = endangle - startangle; } else { anglediff = endangle + ((2 * Math.PI) - startangle); } angle = Dewlib.RestrictRange(t * anglediff + startangle, 0, 2 * Math.PI); } else { if (startangle - endangle > 0) { anglediff = startangle - endangle; } else { anglediff = startangle + ((2 * Math.PI) - endangle); } angle = Dewlib.RestrictRange(-t * anglediff + startangle, 0, 2 * Math.PI); } Point accessed = new Point(); accessed.x = center.x + radius * Math.Cos(angle); accessed.y = center.y + radius * Math.Sin(angle); return(accessed); }
public DebugController() { //Checks if the file exists, then loads it if it does if (File.Exists("debugcommands.txt")) { using (StreamReader sr = new StreamReader("debugcommands.txt")) { string filecontents = sr.ReadToEnd(); commandlist = filecontents.Split('\n'); commandlist = Dewlib.TrimStringArray(commandlist); } this.FindCommands(); } else { commandlist = new string[0]; } }
// Searches for a tag in a given section. // This method does not search for info in events, timingpoints, colours, or hitobjects. // If the tag is not found, the method returns null. public string GetTag(string sectionname, string tag) { sectionname = sectionname.ToUpper(); //Get the correct line number for the given section int sectionline = section.GetTaggableSectionLine(sectionname); //If the section doesn't exist, or is not a taggable section, return null if (sectionline == -1) { return(null); } //Searches through each line for the requested tag for (int i = sectionline + 1; i < filelines.Length; i++) { //Section ends on empty line, so stop searching once you get to one if (filelines[i].Length == 0) { break; } //Get a pair of strings, one side is the tag, other side is the value of the tag string[] pair = Dewlib.SplitFirst(filelines[i], ':'); //Skip if the pair is invalid if (pair.Length != 2) { continue; } //Trim the pair, since the tag value will have a space (e.g. Mode: 0) pair = Dewlib.TrimStringArray(pair); //Essentially if the tag is a match if (pair[0].ToUpper() == tag.ToUpper()) { //Return its value return(pair[1]); } } //If nothing was found, return null return(null); }
public Point GetPointAlong(int along) { //Make the number of steps 1000 for each curve double steps = 1000 / controlpoints.Count - 2 - 1; //how much to increment t by with every loop double increment = 1 / steps; //how much along the curve we have traveled so far double length = 0; //where to get the next point on a given curve //assign increment to get the next intended point double t = increment; //track which curve (defined by two points) is being looked at //start at 1 to not break tangent points int curvestartpoint = 1; Point prev = controlpoints[0]; //Subtract two for the extra points to get the number of curves while (curvestartpoint < controlpoints.Count - 2) { Point next = GetPointBetween(curvestartpoint, curvestartpoint + 1, t); double distance = Dewlib.GetDistance(prev.x, prev.y, next.x, next.y); length += distance; prev = next; if (length >= along) { return(next); } t += increment; if (t > 1) { curvestartpoint++; t -= 1; } } //If we reached the end of the slider without accumulated sliderlength distance, //just assume that the last point is the last point of the curve return(GetPointBetween(controlpoints.Count - 2 - 1, controlpoints.Count - 2, 1)); }
//Calculates the Milliseconds per Beat at a specified time by searching //through the entire timing points section //Timing points inside sliders don't affect the slider itself protected double GetMpB() { int ms = Int32.Parse(HitObjectParser.GetProperty(id, "time")); //Get all the timing sections of the beatmap string[] timings = this.map.GetSection("TimingPoints"); //Just in case there is only one timing point string timingpoint = timings[0]; //Find the section that applies to the given time for (int i = 0; i < timings.Length; i++) { //Split the string by commas to get all the relevant times string[] attributes = timings[i].Split(','); //Trim each string just in case attributes = Dewlib.TrimStringArray(attributes); if (Int32.Parse(attributes[0]) > ms) { break; } else if (Double.Parse(attributes[1], CultureInfo.InvariantCulture) > 0) { timingpoint = timings[i]; } else { continue; } } if (timingpoint == null) { throw new Exception("Error, no relevant timing point\nms=" + ms); } string[] properties = timingpoint.Split(','); return(Double.Parse(properties[1], CultureInfo.InvariantCulture)); }
private void CalculateCatcherSize() { CS = Dewlib.Clamp(CS, 0, 10); //Calculate the integer part of CS first, then modify later based on the decimal double size = 144 - 12 * Math.Floor(CS); //If CS has a decimal place if (CS % 1 != 0) { //Avoid precision bugs - round to one decimal place double CSdecimal = Math.Round(CS - Math.Floor(CS), 1); if (CSdecimal == 0.2) { size -= 2; } else if (0.3 <= CSdecimal && CSdecimal <= 0.4) { size -= 4; } else if (0.5 <= CSdecimal && CSdecimal <= 0.6) { size -= 6; } else if (CSdecimal == 0.7) { size -= 8; } else if (0.8 <= CSdecimal && CSdecimal <= 0.9) { size -= 10; } } catcherwidth = (int)size; }
//Uses the given list of control points to construct a list of bezier curves //to account for red points public BezierSlider(string id, Beatmap amap) : base(id, amap) { //Get the initial hit point of the slider //Split into three lines for readibility Point initialcoord = new Point(); initialcoord.x = Int32.Parse(HitObjectParser.GetProperty(id, "x")); initialcoord.y = Int32.Parse(HitObjectParser.GetProperty(id, "y")); List <BezierCurve> accumulatedcurves = new List <BezierCurve>(); List <Point> allcontrolpoints = new List <Point>(); allcontrolpoints.Add(initialcoord); allcontrolpoints.AddRange(controlpoints); Point[][] curvepoints = Dewlib.SplitPointList(allcontrolpoints.ToArray()); foreach (Point[] curve in curvepoints) { accumulatedcurves.Add(new BezierCurve(curve)); } curves = accumulatedcurves.ToArray(); }
//Gets the distance between the start point and end point //Note that this is NOT the length of the slider public double DistanceBetween() { return(Dewlib.GetDistance(begin.x, begin.y, end.x, end.y)); }
/// <summary> /// Format the file's contents into an array (each entry is one line), loads it into this.filelines, and trims each entry of whitespace. /// Empty lines are preserved (to determine when a section ends) /// </summary> /// <param name="filecontents">The contents of the file in a string</param> private void FormatFileString(string filecontents) { filelines = filecontents.Split('\n'); filelines = Dewlib.TrimStringArray(filelines); }
protected override int[] GetTickLocations() { //Necessary to avoid cases where the pixellength is something like 105.000004005432 int length = Convert.ToInt32(Math.Floor(Double.Parse(HitObjectParser.GetProperty(id, "pixelLength"), CultureInfo.InvariantCulture))); int sliderruns = Int32.Parse(HitObjectParser.GetProperty(id, "repeat")); //Only need ticks for one slider length (no repeats needed) //Also no need for double conversion since TickCount is always divisible by sliderruns int tickcount = this.GetTickCount() / sliderruns; double slidervelocity = this.GetSliderVelocity(); double tickrate = Double.Parse(map.GetTag("Difficulty", "SliderTickRate"), CultureInfo.InvariantCulture); int ticklength = (int)Math.Round(slidervelocity * (100 / tickrate)); if (length <= ticklength) { return(new int[0]); } List <Point> ticks = new List <Point>(); //how many steps to travel through the curve //divide by curves.Length to scale this with the number of curves double steps = length * 2 / curves.Length; //how much to increment t by with every loop double increment = 1 / steps; //how much along the curve we have traveled so far double travelled = 0; //where to get the next point on a given curve //assign increment to get the next intended point double t = increment; Point prev = new Point(); prev.x = Int32.Parse(HitObjectParser.GetProperty(id, "x")); prev.y = Int32.Parse(HitObjectParser.GetProperty(id, "y")); //which curve we are looking at int curvenumber = 0; while (curvenumber < curves.Length) { Point next = curves[curvenumber].Bezier(t); double distance = Dewlib.GetDistance(prev.x, prev.y, next.x, next.y); travelled += distance; prev = next; if (travelled >= ticklength) { ticks.Add(next); travelled = 0; if (ticks.Count == tickcount) { break; } } t += increment; if (t > 1) { curvenumber++; t -= 1; } } if (travelled > 0) { throw new Exception("Error, too many ticks to get in bezier curve, travelled=" + travelled); } List <int> locations = new List <int>(); foreach (Point i in ticks) { locations.Add(i.IntX()); } return(locations.ToArray()); }