private double calcCircleSegmentLength(Point2 from, Point2 to, Point2 mid) { Circle circle = new Circle(from, to, mid); Point2 center = circle.center; double radius = circle.radius; double angle1 = PatternGenerator.calcAngle(from, center); double angle2 = PatternGenerator.calcAngle(to, center); return(Math.Abs(angle2 - angle1) * radius); }
private Point2 binarySeachPoint(Point2 from, double angle, int distToNextPoint, double shift_delta) { double high = distToNextPoint; double low = 0; double eps = 1e-3; Point2 to = null; while (high - low > eps) { double current = (low + high) / 2; to = new Point2(from.X + (float)(current * Math.Cos(angle)), from.Y + (float)(current * Math.Sin(angle))); var mid = new Point2((to.X + from.X) / 2, (to.Y + from.Y) / 2); mid.X += (float)(shift_delta * Math.Sin(angle)); mid.Y += (float)(shift_delta * Math.Cos(angle)); if (!PatternGenerator.checkCoordinateLimits(mid)) { mid.X -= 2 * (float)(shift_delta * Math.Sin(angle)); mid.Y -= 2 * (float)(shift_delta * Math.Cos(angle)); } double spacing = calcCircleSegmentLength(from, to, mid); if (spacing < distToNextPoint) { low = current; } else { high = current; } } return(to); }
public List <CircleObject> generatePolygons(MapContextAwareness mapContext, int points, int number, int spacing, int rotation, int shift, bool randomize) { var result = new List <CircleObject>(); double angle = 0; int X = mapContext.X; int Y = mapContext.Y; int shiftx = shift; int shifty = shift; for (int i = 0; i < number && mapContext.Offset < mapContext.endOffset; ++i) { Point2 pp = checkPolygonialBounds(X, Y, spacing); if (!ReferenceEquals(pp, null)) { X = (int)pp.X; Y = (int)pp.Y; } var ps = PatternGenerator.polygon(points, new Point2(X, Y), spacing, angle); if (randomize) { //ps = PatternGenerator.shuffleOrder(ps); ps = reoderIntoStar(ps); } foreach (var p in ps) { p.Type = BMAPI.v1.HitObjectType.Circle; } ps[0].Type |= BMAPI.v1.HitObjectType.NewCombo; foreach (var obj in ps) { obj.StartTime = (int)mapContext.Offset; mapContext.Offset += mapContext.bpm; } result.AddRange(ps); angle += PatternGenerator.ConvertToRadians(rotation); if (!PatternGenerator.checkCoordinateLimits(X, Y, points, spacing, shiftx, shifty)) { //TODO: add normalization Point2 next = PatternGenerator.findNextPosition(X, Y, shift); shiftx = (int)(next.X - X); shifty = (int)(next.Y - Y); } X += shiftx; Y += shifty; } double nextShift = Math.Sqrt(2 * spacing * spacing * (1.0 - Math.Cos(Math.PI * 2 / points))); Point2 nextPosition = PatternGenerator.findNextPosition(X, Y, nextShift); mapContext.X = (int)nextPosition.X; mapContext.Y = (int)nextPosition.Y; return(result); }
public List <CircleObject> generateHorizontalPattern(MapContextAwareness mapContext) { if (end) { number = (int)1e6; } var result = new List <CircleObject>(); double angle = rotation; int X = mapContext.X; int Y = mapContext.Y; int shiftx = 0; int shifty = shift; for (int i = 0; i < number && mapContext.Offset < mapContext.endOffset; ++i) { Point2 pp = checkHorizontalJumpBounds(X, Y, spacing, angle); if (!ReferenceEquals(pp, null)) { X = (int)pp.X; Y = (int)pp.Y; } var ps = PatternGenerator.generateHorizontalJump(new Point2(X, Y), spacing, angle, randomize); if (randomize) { ps = PatternGenerator.shuffleOrder(ps); } foreach (var p in ps) { p.Type = BMAPI.v1.HitObjectType.Circle; } ps[0].Type |= BMAPI.v1.HitObjectType.NewCombo; foreach (var obj in ps) { obj.StartTime = (int)mapContext.Offset; mapContext.Offset += mapContext.bpm; } result.AddRange(ps); if (checkNextJumpBounds(X + shiftx, Y + shifty, angle)) { shifty = -shifty; } X += shiftx; Y += shifty; } double nextShift = shift; Point2 nextPosition = PatternGenerator.findNextPosition(X, Y, nextShift); mapContext.X = (int)nextPosition.X; mapContext.Y = (int)nextPosition.Y; return(result); }
private bool checkNextJumpBounds(int X, int Y, double angle) { return(!PatternGenerator.checkCoordinateLimits(X, Y) && !ReferenceEquals(checkVerticalJumpBounds(X, Y, spacing, angle), null)); }
private List <CircleObject> generateRotatingPattern(MapContextAwareness mapContext) { if (end) { number = (int)1e6; } var result = new List <CircleObject>(); double angle = 0; int X = mapContext.X; int Y = mapContext.Y; int shiftx = shift; int shifty = shift; for (int i = 0; i < number && mapContext.Offset < mapContext.endOffset; ++i) { Point2 pp = checkVerticalJumpBounds(X, Y, spacing, angle); if (!ReferenceEquals(pp, null)) { X = (int)pp.X; Y = (int)pp.Y; } var ps = PatternGenerator.generateRotationalJump(new Point2(X, Y), spacing, angle, randomize); if (randomize) { ps = PatternGenerator.shuffleOrder(ps); } foreach (var p in ps) { p.Type = BMAPI.v1.HitObjectType.Circle; } ps[0].Type |= BMAPI.v1.HitObjectType.NewCombo; foreach (var obj in ps) { obj.StartTime = (int)mapContext.Offset; mapContext.Offset += mapContext.bpm; } result.AddRange(ps); angle += rotation; if (checkNextJumpBounds(X + shiftx, Y + shifty, angle)) { Point2 next = PatternGenerator.findNextPosition(X, Y, shift); shiftx = (int)(next.X - X); shifty = (int)(next.Y - Y); double norm = Math.Sqrt(Utils.sqr(shiftx) + Utils.sqr(shifty)); double recommended_shift = Math.Sqrt(2 * Utils.sqr(shift)); shiftx = (int)(shiftx / norm * recommended_shift); shifty = (int)(shifty / norm * recommended_shift); } X += shiftx; Y += shifty; } double nextShift = shift; Point2 nextPosition = PatternGenerator.findNextPosition(X, Y, nextShift); mapContext.X = (int)nextPosition.X; mapContext.Y = (int)nextPosition.Y; return(result); }
public List <CircleObject> generateStreams(MapContextAwareness mapContext, int points, int number, int spacing, int shift) { //spacing is the distance between two notes in the stream if (end) { //double endOffset = mapContext.endOffset; //double currOffset = mapContext.Offset; //int n = (int)Math.Floor((endOffset - currOffset) / mapContext.bpm * 2 / points) - 1; number = (int)1e6; } List <CircleObject> result = new List <CircleObject>(); int shiftx = shift; int shifty = shift; double angle = 0; bool flag = false; int distToNextPoint = spacing * (points - 1); for (int i = 0; i < number && mapContext.Offset < mapContext.endOffset; ++i) { Point2 from = new Point2(mapContext.X, mapContext.Y); double tmp_angle = angle; Point2 to = new Point2(from.X + distToNextPoint * (float)Math.Cos(angle), from.Y + distToNextPoint * (float)Math.Sin(angle)); if (!PatternGenerator.checkCoordinateLimits(to)) { flag = true; } int tmp_spacing = distToNextPoint; for (int k = 0; flag; ++k) { tmp_angle = Utils.rng.NextDouble() * Math.PI * 2; while (Math.Abs(Math.Tan(angle) - Math.Tan(tmp_angle)) < 0.05) { tmp_angle = Utils.rng.NextDouble() * Math.PI * 2; } to = new Point2(from.X + tmp_spacing * (float)Math.Cos(tmp_angle), from.Y + tmp_spacing * (float)Math.Sin(tmp_angle)); if (PatternGenerator.checkCoordinateLimits(to)) { flag = false; } else { if (k > 1000) { to = pointToCenter(from, tmp_spacing); tmp_spacing = (int)(tmp_spacing * 0.9); k = 0; } } } double mid_delta = generateMidDelta(); List <CircleObject> pattern = null; if (mid_delta > 0.1) { double shift_delta = mid_delta * distToNextPoint; to = binarySeachPoint(from, tmp_angle, distToNextPoint, shift_delta); var mid = new Point2((to.X + from.X) / 2, (to.Y + from.Y) / 2); mid.X += (float)(shift_delta * Math.Sin(tmp_angle)); mid.Y += (float)(shift_delta * Math.Cos(tmp_angle)); if (!PatternGenerator.checkCoordinateLimits(mid)) { mid.X -= 2 * (float)(shift_delta * Math.Sin(tmp_angle)); mid.Y -= 2 * (float)(shift_delta * Math.Cos(tmp_angle)); } pattern = PatternGenerator.stream(points, from, to, mid); } else { pattern = PatternGenerator.stream(points, from, to); } pattern[0].Type |= BMAPI.v1.HitObjectType.NewCombo; foreach (var obj in pattern) { obj.StartTime = (int)mapContext.Offset; mapContext.Offset += mapContext.bpm / 2; } result.AddRange(pattern); double spacing_shift = spacing; double recommended_shift = Math.Max(spacing_shift, shift); if (!PatternGenerator.checkCoordinateLimits(to.X + shiftx, to.Y + shifty)) { Point2 next = PatternGenerator.findNextPosition(mapContext.X, mapContext.Y, recommended_shift); shiftx = (int)(next.X - mapContext.X); shifty = (int)(next.Y - mapContext.Y); double norm = Math.Sqrt(Utils.sqr(shiftx) + Utils.sqr(shifty)); if (norm < 0.1) { norm = 1; } shiftx = (int)(shiftx / norm * recommended_shift); shifty = (int)(shifty / norm * recommended_shift); } mapContext.X = (int)to.X + shiftx; mapContext.Y = (int)to.Y + shifty; angle = tmp_angle; } return(result); }
public List <CircleObject> generateRandomJumps(MapContextAwareness mapContext, int numberOfNotes, int spacing) { var result = new List <CircleObject>(); int c = 0; for (int i = 0; i < numberOfNotes && mapContext.Offset < mapContext.endOffset; ++i) { Point2 next; if (i > 0) { next = PatternGenerator.findNextPosition(mapContext.X, mapContext.Y, spacing); } else { next = PatternGenerator.findNextPosition(mapContext.X, mapContext.Y, 0); } int stackNumber; if (useOnly) { stackNumber = maxStack; } else { stackNumber = Utils.rng.Next(1, maxStack + 1); } for (int j = 0; j < stackNumber; ++j) { CircleObject note = new CircleObject(); if (j == 0) { note.Type |= BMAPI.v1.HitObjectType.NewCombo; } note.Location = next; result.Add(note); mapContext.X = (int)next.X; mapContext.Y = (int)next.Y; note.StartTime = (int)mapContext.Offset; if (j < stackNumber - 1) { mapContext.Offset += mapContext.bpm / 2; } } if (stackNumber % 2 == 0) { ++c; } mapContext.Offset += mapContext.bpm; } if (c % 2 == 1) { mapContext.Offset += mapContext.bpm / 2; } return(result); }
private void extractMapContextFromWindow(InitialSettingsWindow window) { TimingPoint current = baseBeatmap.TimingPoints[0]; //double generator_bpm = current.BpmDelay / 2; string tickDivisor = window.tickDivisorComboBox.Text; double bpm_multiplier; switch (tickDivisor) { case "1/1": bpm_multiplier = 2; break; case "1/2": bpm_multiplier = 1; break; case "1/3": bpm_multiplier = 2.0 / 3; break; case "1/4": bpm_multiplier = 0.5; break; default: throw new Exception("Unknown tick divisor"); } double generator_beginOffset; if (window.firstTimingCheckbox.IsChecked.Value) { generator_beginOffset = current.Time; } else { double tmp; if (double.TryParse(window.beginOffsetTextbox.Text, out tmp)) { generator_beginOffset = tmp; } else { MessageBox.Show("Unable to parse the begin offset. Please input a valid number or check the First timing point checkbox"); return; } } double generator_endOffset; if (window.lastObjectCheckbox.IsChecked.Value) { generator_endOffset = findLastObjectTimingInMap(baseBeatmap); } else { double tmp; if (double.TryParse(window.endOffsetTextbox.Text, out tmp)) { generator_endOffset = tmp; } else { MessageBox.Show("Unable to parse the begin offset. Please input a valid number or check the Last object checkbox"); return; } } int generator_X; int generator_Y; if (window.overrideStartPointCheckBox.IsChecked ?? true) { double tmp; if (double.TryParse(window.XtextBox.Text, out tmp)) { generator_X = (int)tmp; } else { MessageBox.Show("Unable to parse the X coordinate. Please input a valid number or uncheck the override starting position checkbox"); return; } if (double.TryParse(window.YtextBox.Text, out tmp)) { generator_Y = (int)tmp; } else { MessageBox.Show("Unable to parse the Y coordinate. Please input a valid number or uncheck the override starting position checkbox"); return; } } else { generator_X = 200; generator_Y = 200; } generator.mapContext = new MapContextAwareness(bpm_multiplier, generator_beginOffset, generator_endOffset, generator_X, generator_Y, baseBeatmap.TimingPoints); if (window.keepOriginalPartCheckbox.IsChecked.Value) { PatternGenerator.copyMap(baseBeatmap, generator.generatedMap, generator.mapContext.Offset, generator.mapContext.endOffset); } }