private void load() { var maniaPlayfield = (ManiaPlayfield)Playfield; double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue; SortedList <TimingControlPoint> timingPoints = Beatmap.ControlPointInfo.TimingPoints; for (int i = 0; i < timingPoints.Count; i++) { TimingControlPoint point = timingPoints[i]; // Stop on the beat before the next timing point, or if there is no next timing point stop slightly past the last object double endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time - point.BeatLength : lastObjectTime + point.BeatLength * (int)point.TimeSignature; int index = 0; for (double t = timingPoints[i].Time; Precision.DefinitelyBigger(endTime, t); t += point.BeatLength, index++) { maniaPlayfield.Add(new DrawableBarLine(new BarLine { StartTime = t, ControlPoint = point, BeatIndex = index })); } } }
/// <summary> /// Calculates the snapped time for a given time and makes sure the snapped time is not outside the time range of a hit object. /// This can be used to resnap stuff that has to be within the time range of a slider. For example volume changes inside a slider body. /// </summary> /// <param name="time"></param> /// <param name="divisor2">The first beat snap divisor.</param> /// <param name="divisor3">The second beat snap divisor.</param> /// <param name="ho">The hit object with a time range that the specified time has to stay inside.</param> /// <param name="floor">Whether or not to floor the time after snapping.</param> /// <param name="tp">The uninherited timing point to snap to. Leave null for automatic selection.</param> /// <param name="firstTp">Overwrites the timing for anything that happens before the first timing point. /// You can set this to avoid bad timing when there could be an inherited timing point before the first red line.</param> /// <returns>The snapped time.</returns> public double ResnapInRange(double time, int divisor2, int divisor3, HitObject ho, bool floor = true, TimingPoint tp = null, TimingPoint firstTp = null) { TimingPoint beforeTp = tp ?? GetRedlineAtTime(time, firstTp); TimingPoint afterTp = tp == null?GetRedlineAfterTime(time) : null; double newTime2 = GetNearestTimeMeter(time, beforeTp, divisor2); double snapDistance2 = Math.Abs(time - newTime2); double newTime3 = GetNearestTimeMeter(time, beforeTp, divisor3); double snapDistance3 = Math.Abs(time - newTime3); double newTime = snapDistance3 < snapDistance2 ? newTime3 : newTime2; if (afterTp != null && Precision.DefinitelyBigger(newTime, afterTp.Offset)) { newTime = afterTp.Offset; } if (newTime <= ho.Time + 1 || newTime >= ho.EndTime - 1) // Don't resnap if it would move outside { newTime = time; } return(floor ? Math.Floor(newTime) : newTime); }
public void TestMouseZoomInTwiceOutTwice() { reset(); AddStep("Press alt down", () => InputManager.PressKey(Key.AltLeft)); // Scroll in at 0.25 AddStep("Move mouse to 0.25x", () => InputManager.MoveMouseTo(new Vector2(scrollQuad.TopLeft.X + 0.25f * scrollQuad.Size.X, scrollQuad.Centre.Y))); AddStep("Scroll by 1", () => InputManager.ScrollBy(new Vector2(0, 1))); // Scroll in at 0.6 AddStep("Move mouse to 0.75x", () => InputManager.MoveMouseTo(new Vector2(scrollQuad.TopLeft.X + 0.75f * scrollQuad.Size.X, scrollQuad.Centre.Y))); AddStep("Scroll by 1", () => InputManager.ScrollBy(new Vector2(0, 1))); AddAssert("Box not at 0", () => !Precision.AlmostEquals(boxQuad.TopLeft, scrollQuad.TopLeft)); // Very hard to determine actual position, so approximate AddAssert("Box at correct position (1)", () => Precision.DefinitelyBigger(scrollQuad.TopLeft.X + 0.25f * scrollQuad.Size.X, boxQuad.TopLeft.X + 0.25f * boxQuad.Size.X)); AddAssert("Box at correct position (2)", () => Precision.DefinitelyBigger(scrollQuad.TopLeft.X + 0.6f * scrollQuad.Size.X, boxQuad.TopLeft.X + 0.3f * boxQuad.Size.X)); AddAssert("Box at correct position (3)", () => Precision.DefinitelyBigger(boxQuad.TopLeft.X + 0.6f * boxQuad.Size.X, scrollQuad.TopLeft.X + 0.6f * scrollQuad.Size.X)); // Scroll out at 0.6 AddStep("Scroll by -1", () => InputManager.ScrollBy(new Vector2(0, -1))); // Scroll out at 0.25 AddStep("Move mouse to 0.25x", () => InputManager.MoveMouseTo(new Vector2(scrollQuad.TopLeft.X + 0.25f * scrollQuad.Size.X, scrollQuad.Centre.Y))); AddStep("Scroll by -1", () => InputManager.ScrollBy(new Vector2(0, -1))); AddAssert("Box at 0", () => Precision.AlmostEquals(boxQuad.TopLeft, scrollQuad.TopLeft)); AddStep("Release alt", () => InputManager.ReleaseKey(Key.AltLeft)); }
protected override void Update() { base.Update(); // the bounds below represent the horizontal range of scroll items to be considered fully visible/active, in the scroll's internal coordinate space. // note that clamping is applied to the left scroll bound to ensure scrolling past extents does not change the set of active columns. float leftVisibleBound = Math.Clamp(Current, 0, ScrollableExtent); float rightVisibleBound = leftVisibleBound + DrawWidth; // if a movement is occurring at this time, the bounds below represent the full range of columns that the scroll movement will encompass. // this will be used to ensure that columns do not change state from active to inactive back and forth until they are fully scrolled past. float leftMovementBound = Math.Min(Current, Target); float rightMovementBound = Math.Max(Current, Target) + DrawWidth; foreach (var column in Child) { // DrawWidth/DrawPosition do not include shear effects, and we want to know the full extents of the columns post-shear, // so we have to manually compensate. var topLeft = column.ToSpaceOfOtherDrawable(Vector2.Zero, ScrollContent); var bottomRight = column.ToSpaceOfOtherDrawable(new Vector2(column.DrawWidth - column.DrawHeight * SHEAR, 0), ScrollContent); bool isCurrentlyVisible = Precision.AlmostBigger(topLeft.X, leftVisibleBound) && Precision.DefinitelyBigger(rightVisibleBound, bottomRight.X); bool isBeingScrolledToward = Precision.AlmostBigger(topLeft.X, leftMovementBound) && Precision.DefinitelyBigger(rightMovementBound, bottomRight.X); column.Active.Value = isCurrentlyVisible || isBeingScrolledToward; } }
private void initialBarLine() { var beatmap = KaraokeRulesetContainer.Beatmap; var objects = beatmap.HitObjects; var lastObjectTime = (objects.LastOrDefault() as IHasEndTime)?.EndTime ?? objects.LastOrDefault()?.StartTime ?? double.MaxValue; var timingPoints = beatmap.ControlPointInfo.TimingPoints; var barLines = new List <BarLine>(); for (var i = 0; i < timingPoints.Count; i++) { var point = timingPoints[i]; var endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time - point.BeatLength : lastObjectTime + point.BeatLength * (int)point.TimeSignature; var index = 0; for (var t = timingPoints[i].Time; Precision.DefinitelyBigger(endTime, t); t += point.BeatLength, index++) { barLines.Add(new BarLine { StartTime = t, ControlPoint = point, BeatIndex = index }); } } BarLines = barLines; BarLines.ForEach(Add); }
public ManiaRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) : base(ruleset, beatmap) { // Generate the bar lines double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue; var timingPoints = Beatmap.ControlPointInfo.TimingPoints; var barLines = new List <BarLine>(); for (int i = 0; i < timingPoints.Count; i++) { TimingControlPoint point = timingPoints[i]; // Stop on the beat before the next timing point, or if there is no next timing point stop slightly past the last object double endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time - point.BeatLength : lastObjectTime + point.BeatLength * (int)point.TimeSignature; int index = 0; for (double t = timingPoints[i].Time; Precision.DefinitelyBigger(endTime, t); t += point.BeatLength, index++) { barLines.Add(new BarLine { StartTime = t, ControlPoint = point, BeatIndex = index }); } } BarLines = barLines; }
/// <summary> /// Method that generates hit objects for non-osu!mania beatmaps. /// </summary> /// <param name="original">The original hit object.</param> /// <param name="originalBeatmap">The original beatmap. This is used to look-up any values dependent on a fully-loaded beatmap.</param> /// <returns>The hit objects generated.</returns> private IEnumerable <ManiaHitObject> generateConverted(HitObject original, IBeatmap originalBeatmap) { Patterns.PatternGenerator conversion = null; switch (original) { case IHasDistance _: { var generator = new DistanceObjectPatternGenerator(Random, original, beatmap, lastPattern, originalBeatmap); conversion = generator; var positionData = original as IHasPosition; for (double time = original.StartTime; !Precision.DefinitelyBigger(time, generator.EndTime); time += generator.SegmentDuration) { recordNote(time, positionData?.Position ?? Vector2.Zero); computeDensity(time); } break; } case IHasEndTime endTimeData: { conversion = new EndTimeObjectPatternGenerator(Random, original, beatmap, originalBeatmap); recordNote(endTimeData.EndTime, new Vector2(256, 192)); computeDensity(endTimeData.EndTime); break; } case IHasPosition positionData: { computeDensity(original.StartTime); conversion = new HitObjectPatternGenerator(Random, original, beatmap, lastPattern, lastTime, lastPosition, density, lastStair, originalBeatmap); recordNote(original.StartTime, positionData.Position); break; } } if (conversion == null) { yield break; } foreach (var newPattern in conversion.Generate()) { lastPattern = conversion is EndTimeObjectPatternGenerator ? lastPattern : newPattern; lastStair = (conversion as HitObjectPatternGenerator)?.StairType ?? lastStair; foreach (var obj in newPattern.HitObjects) { yield return(obj); } } }
/// <summary> /// Calculates the size of the effective time range of a given timing point. /// This range stops at the next timing point, so it just returns the offset of the next timing point. /// </summary> /// <param name="timingPoint"></param> /// <returns>The timing point after specified timing point.</returns> public double GetTimingPointEffectiveRange(TimingPoint timingPoint) { foreach (var tp in TimingPoints.Where(tp => Precision.DefinitelyBigger(tp.Offset, timingPoint.Offset))) { return(tp.Offset); } return(double.PositiveInfinity); // Being the last timingpoint, the effective range is infinite (very big) }
public void TestOpacity() { AddMoveStep(100, 0); AddClickStep(MouseButton.Left); AddUntilStep("outline is semitransparent", () => Precision.DefinitelyBigger(1, timeSpanOutline.Alpha)); AddMoveStep(200, 0); AddUntilStep("outline is opaque", () => Precision.AlmostEquals(timeSpanOutline.Alpha, 1)); AddMoveStep(100, 0); AddUntilStep("outline is semitransparent", () => Precision.DefinitelyBigger(1, timeSpanOutline.Alpha)); }
public ManiaHitRenderer(WorkingBeatmap beatmap, bool isForCurrentRuleset) : base(beatmap, isForCurrentRuleset) { // Generate the speed adjustment container lists hitObjectSpeedAdjustments = new List <SpeedAdjustmentContainer> [PreferredColumns]; for (int i = 0; i < PreferredColumns; i++) { hitObjectSpeedAdjustments[i] = new List <SpeedAdjustmentContainer>(); } // Generate the bar lines double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue; SortedList <TimingControlPoint> timingPoints = Beatmap.ControlPointInfo.TimingPoints; var barLines = new List <DrawableBarLine>(); for (int i = 0; i < timingPoints.Count; i++) { TimingControlPoint point = timingPoints[i]; // Stop on the beat before the next timing point, or if there is no next timing point stop slightly past the last object double endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time - point.BeatLength : lastObjectTime + point.BeatLength * (int)point.TimeSignature; int index = 0; for (double t = timingPoints[i].Time; Precision.DefinitelyBigger(endTime, t); t += point.BeatLength, index++) { barLines.Add(new DrawableBarLine(new BarLine { StartTime = t, ControlPoint = point, BeatIndex = index })); } } BarLines = barLines; // Generate speed adjustments from mods first bool useDefaultSpeedAdjustments = true; if (Mods != null) { foreach (var speedAdjustmentMod in Mods.OfType <IGenerateSpeedAdjustments>()) { useDefaultSpeedAdjustments = false; speedAdjustmentMod.ApplyToHitRenderer(this, ref hitObjectSpeedAdjustments, ref barLineSpeedAdjustments); } } // Generate the default speed adjustments if (useDefaultSpeedAdjustments) { generateDefaultSpeedAdjustments(); } }
protected override double StrainValueOf(DifficultyHitObject current) { var maniaCurrent = (ManiaDifficultyHitObject)current; double endTime = maniaCurrent.EndTime; int column = maniaCurrent.BaseObject.Column; double closestEndTime = Math.Abs(endTime - maniaCurrent.LastObject.StartTime); // Lowest value we can assume with the current information double holdFactor = 1.0; // Factor to all additional strains in case something else is held double holdAddition = 0; // Addition to the current note in case it's a hold and has to be released awkwardly bool isOverlapping = false; // Fill up the holdEndTimes array for (int i = 0; i < holdEndTimes.Length; ++i) { // The current note is overlapped if a previous note or end is overlapping the current note body isOverlapping |= Precision.DefinitelyBigger(holdEndTimes[i], maniaCurrent.StartTime, 1) && Precision.DefinitelyBigger(endTime, holdEndTimes[i], 1); // We give a slight bonus to everything if something is held meanwhile if (Precision.DefinitelyBigger(holdEndTimes[i], endTime, 1)) { holdFactor = 1.25; } closestEndTime = Math.Min(closestEndTime, Math.Abs(endTime - holdEndTimes[i])); // Decay individual strains individualStrains[i] = applyDecay(individualStrains[i], current.DeltaTime, individual_decay_base); } holdEndTimes[column] = endTime; // The hold addition is given if there was an overlap, however it is only valid if there are no other note with a similar ending. // Releasing multiple notes is just as easy as releasing 1. Nerfs the hold addition by half if the closest release is release_threshold away. // holdAddition // ^ // 1.0 + - - - - - -+----------- // | / // 0.5 + - - - - -/ Sigmoid Curve // | /| // 0.0 +--------+-+---------------> Release Difference / ms // release_threshold if (isOverlapping) { holdAddition = 1 / (1 + Math.Exp(0.5 * (release_threshold - closestEndTime))); } // Increase individual strain in own column individualStrains[column] += 2.0 * holdFactor; individualStrain = individualStrains[column]; overallStrain = applyDecay(overallStrain, current.DeltaTime, overall_decay_base) + (1 + holdAddition) * holdFactor; return(individualStrain + overallStrain - CurrentStrain); }
private bool shouldUpdateHue(float newHue) { // there are two situations in which a hue value change is possibly unwanted. // * if saturation is near-zero, it may not be really possible to accurately measure the hue of the colour, // as hsv(x, 0, y) == hsv(z, 0, y) for any x,y,z. // * similarly, the hues of 0 and 1 are functionally equivalent, // as hsv(0, x, y) == hsv(1, x, y) for any x,y. // in those cases, just keep the hue as it was, as the colour will still be roughly the same to the point of being imperceptible, // and doing this will prevent UX idiosyncrasies (such as the hue slider jumping to 0 for no apparent reason). return(Precision.DefinitelyBigger(Saturation.Value, 0) && !Precision.AlmostEquals(Hue.Value - newHue, 1)); }
/// <summary> /// Finds the timing point which is in effect at a given time with a custom set of timing points. /// </summary> /// <param name="time"></param> /// <param name="timingPoints">All the timing points.</param> /// <param name="firstTimingpoint">The first timing point to start searching from.</param> /// <returns></returns> public static TimingPoint GetTimingPointAtTime(double time, List <TimingPoint> timingPoints, TimingPoint firstTimingpoint) { TimingPoint lastTp = firstTimingpoint; foreach (TimingPoint tp in timingPoints) { if (Precision.DefinitelyBigger(tp.Offset, time)) { return(lastTp); } lastTp = tp; } return(lastTp); }
private void convertToStream() { if (editorBeatmap == null || changeHandler == null || beatDivisor == null) { return; } var timingPoint = editorBeatmap.ControlPointInfo.TimingPointAt(HitObject.StartTime); double streamSpacing = timingPoint.BeatLength / beatDivisor.Value; changeHandler.BeginChange(); int i = 0; double time = HitObject.StartTime; while (!Precision.DefinitelyBigger(time, HitObject.GetEndTime(), 1)) { // positionWithRepeats is a fractional number in the range of [0, HitObject.SpanCount()] // and indicates how many fractional spans of a slider have passed up to time. double positionWithRepeats = (time - HitObject.StartTime) / HitObject.Duration * HitObject.SpanCount(); double pathPosition = positionWithRepeats - (int)positionWithRepeats; // every second span is in the reverse direction - need to reverse the path position. if (Precision.AlmostBigger(positionWithRepeats % 2, 1)) { pathPosition = 1 - pathPosition; } Vector2 position = HitObject.Position + HitObject.Path.PositionAt(pathPosition); var samplePoint = (SampleControlPoint)HitObject.SampleControlPoint.DeepClone(); samplePoint.Time = time; editorBeatmap.Add(new HitCircle { StartTime = time, Position = position, NewCombo = i == 0 && HitObject.NewCombo, SampleControlPoint = samplePoint, Samples = HitObject.HeadCircle.Samples.Select(s => s.With()).ToList() }); i += 1; time = HitObject.StartTime + i * streamSpacing; } editorBeatmap.Remove(HitObject); changeHandler.EndChange(); }
/// <summary> /// Constructs and generates bar lines for provided beatmap. /// </summary> /// <param name="beatmap">The beatmap to generate bar lines for.</param> public BarLineGenerator(IBeatmap beatmap) { if (beatmap.HitObjects.Count == 0) { return; } HitObject lastObject = beatmap.HitObjects.Last(); double lastHitTime = 1 + lastObject.GetEndTime(); var timingPoints = beatmap.ControlPointInfo.TimingPoints; if (timingPoints.Count == 0) { return; } for (int i = 0; i < timingPoints.Count; i++) { TimingControlPoint currentTimingPoint = timingPoints[i]; int currentBeat = 0; // Stop on the beat before the next timing point, or if there is no next timing point stop slightly past the last object double endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time - currentTimingPoint.BeatLength : lastHitTime + currentTimingPoint.BeatLength * currentTimingPoint.TimeSignature.Numerator; double barLength = currentTimingPoint.BeatLength * currentTimingPoint.TimeSignature.Numerator; for (double t = currentTimingPoint.Time; Precision.DefinitelyBigger(endTime, t); t += barLength, currentBeat++) { double roundedTime = Math.Round(t, MidpointRounding.AwayFromZero); // in the case of some bar lengths, rounding errors can cause t to be slightly less than // the expected whole number value due to floating point inaccuracies. // if this is the case, apply rounding. if (Precision.AlmostEquals(t, roundedTime)) { t = roundedTime; } BarLines.Add(new TBarLine { StartTime = t, Major = currentBeat % currentTimingPoint.TimeSignature.Numerator == 0 }); } } }
/// <summary> /// Finds the uninherited <see cref="TimingPoint"/> which is in effect at a given time. /// </summary> /// <param name="time"></param> /// <param name="firstTimingPoint"></param> /// <returns></returns> public TimingPoint GetRedlineAtTime(double time, TimingPoint firstTimingPoint = null) { TimingPoint lastTp = firstTimingPoint ?? GetFirstTimingPointExtended(); foreach (TimingPoint tp in TimingPoints) { if (Precision.DefinitelyBigger(tp.Offset, time)) { return(lastTp); } if (tp.Uninherited) { lastTp = tp; } } return(lastTp); }
protected override double StrainValueOf(DifficultyHitObject current) { var maniaCurrent = (ManiaDifficultyHitObject)current; var endTime = maniaCurrent.BaseObject.GetEndTime(); var column = maniaCurrent.BaseObject.Column; double holdFactor = 1.0; // Factor to all additional strains in case something else is held double holdAddition = 0; // Addition to the current note in case it's a hold and has to be released awkwardly // Fill up the holdEndTimes array for (int i = 0; i < holdEndTimes.Length; ++i) { // If there is at least one other overlapping end or note, then we get an addition, buuuuuut... if (Precision.DefinitelyBigger(holdEndTimes[i], maniaCurrent.BaseObject.StartTime, 1) && Precision.DefinitelyBigger(endTime, holdEndTimes[i], 1)) { holdAddition = 1.0; } // ... this addition only is valid if there is _no_ other note with the same ending. Releasing multiple notes at the same time is just as easy as releasing 1 if (Precision.AlmostEquals(endTime, holdEndTimes[i], 1)) { holdAddition = 0; } // We give a slight bonus to everything if something is held meanwhile if (Precision.DefinitelyBigger(holdEndTimes[i], endTime, 1)) { holdFactor = 1.25; } // Decay individual strains individualStrains[i] = applyDecay(individualStrains[i], current.DeltaTime, individual_decay_base); } holdEndTimes[column] = endTime; // Increase individual strain in own column individualStrains[column] += 2.0 * holdFactor; individualStrain = individualStrains[column]; overallStrain = applyDecay(overallStrain, current.DeltaTime, overall_decay_base) + (1 + holdAddition) * holdFactor; return(individualStrain + overallStrain - CurrentStrain); }
/// <summary> /// Returns the beat snap divisor closest to the given time. If two are equally close, the smallest divisor is returned. /// </summary> /// <param name="time">The time to find the closest beat snap divisor to.</param> /// <param name="referenceTime">An optional reference point to use for timing point lookup.</param> public int GetClosestBeatDivisor(double time, double?referenceTime = null) { TimingControlPoint timingPoint = TimingPointAt(referenceTime ?? time); int closestDivisor = 0; double closestTime = double.MaxValue; foreach (int divisor in BindableBeatDivisor.VALID_DIVISORS) { double distanceFromSnap = Math.Abs(time - getClosestSnappedTime(timingPoint, time, divisor)); if (Precision.DefinitelyBigger(closestTime, distanceFromSnap)) { closestDivisor = divisor; closestTime = distanceFromSnap; } } return(closestDivisor); }
public void TestBasicComponentLayout() { double[] times = { 100, 300, 500 }; float[] positions = { 100, 200, 100 }; addBlueprintStep(times, positions); for (int i = 0; i < times.Length; i++) addVertexCheckStep(times.Length, i, times[i], positions[i]); AddAssert("correct outline count", () => { int expected = hitObject.NestedHitObjects.Count(h => !(h is TinyDroplet)); return this.ChildrenOfType<FruitOutline>().Count() == expected; }); AddAssert("correct vertex piece count", () => this.ChildrenOfType<VertexPiece>().Count() == times.Length); AddAssert("first vertex is semitransparent", () => Precision.DefinitelyBigger(1, this.ChildrenOfType<VertexPiece>().First().Alpha)); }
/// <summary> /// Gets the slider velocity at a given time. /// This gives the value from the .osu. /// Ranges from -1000 to -10. /// </summary> /// <param name="time"></param> /// <returns></returns> public double GetSvAtTime(double time) { double lastSv = -100; foreach (TimingPoint tp in TimingPoints) { if (Precision.DefinitelyBigger(tp.Offset, time)) { return(MathHelper.Clamp(lastSv, -1000, -10)); } if (!tp.Uninherited) { lastSv = tp.MpB; } else { lastSv = -100; } } return(MathHelper.Clamp(lastSv, -1000, -10)); }
public void TestWideImageNotExceedContainer() { AddStep("Add image", () => { markdownContainer.CurrentPath = "https://dev.ppy.sh/wiki/osu!_Program_Files/"; markdownContainer.Text = "![](img/file_structure.jpg \"The file structure of osu!'s installation folder, on Windows and macOS\")"; }); AddUntilStep("Wait image to load", () => markdownContainer.ChildrenOfType <DelayedLoadWrapper>().First().DelayedLoadCompleted); AddStep("Change container width", () => { markdownContainer.Width = 0.5f; }); AddAssert("Image not exceed container width", () => { var spriteImage = markdownContainer.ChildrenOfType <Sprite>().First(); return(Precision.DefinitelyBigger(markdownContainer.DrawWidth, spriteImage.DrawWidth)); }); }
/// <summary> /// Constructs and generates bar lines for provided beatmap. /// </summary> /// <param name="beatmap">The beatmap to generate bar lines for.</param> public BarLineGenerator(IBeatmap beatmap) { if (beatmap.HitObjects.Count == 0) { return; } HitObject lastObject = beatmap.HitObjects.Last(); double lastHitTime = 1 + lastObject.GetEndTime(); var timingPoints = beatmap.ControlPointInfo.TimingPoints; if (timingPoints.Count == 0) { return; } for (int i = 0; i < timingPoints.Count; i++) { TimingControlPoint currentTimingPoint = timingPoints[i]; int currentBeat = 0; // Stop on the beat before the next timing point, or if there is no next timing point stop slightly past the last object double endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time - currentTimingPoint.BeatLength : lastHitTime + currentTimingPoint.BeatLength * (int)currentTimingPoint.TimeSignature; double barLength = currentTimingPoint.BeatLength * (int)currentTimingPoint.TimeSignature; for (double t = currentTimingPoint.Time; Precision.DefinitelyBigger(endTime, t); t += barLength, currentBeat++) { BarLines.Add(new TBarLine { StartTime = t, Major = currentBeat % (int)currentTimingPoint.TimeSignature == 0 }); } } }
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 || !(ho2.IsSlider || ho2.IsCircle) || !(ho1.IsSlider || ho1.IsCircle)) { 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) { if (Precision.DefinitelyBigger(dist, 0)) { 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) { if (Precision.DefinitelyBigger(dist, 0)) { 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) { if (Precision.DefinitelyBigger(dist, 0)) { 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 (mergeLast && arg.Leniency == 727) { ho1.SetAllCurvePoints(MakePenis(ho1.GetAllCurvePoints(), ho1.PixelLength)); ho1.PixelLength *= 2; ho1.SliderType = PathType.Bezier; } } // 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 var message = ""; if (Math.Abs(slidersMerged) == 1) { message += "Successfully merged " + slidersMerged + " slider!"; } else { message += "Successfully merged " + slidersMerged + " sliders!"; } return(arg.Quick ? "" : message); }
private static bool definitelyBigger(double value1, double value2) { return(Precision.DefinitelyBigger(value1, value2, timing_precision)); }
/// <summary> /// Finds all the timing points in a specified time range. /// </summary> /// <param name="startTime"></param> /// <param name="endTime"></param> /// <returns></returns> public List <TimingPoint> GetTimingPointsInTimeRange(double startTime, double endTime) { return(TimingPoints.Where(tp => Precision.DefinitelyBigger(tp.Offset, startTime) && Precision.DefinitelyBigger(endTime, tp.Offset)).ToList()); }
/// <summary> /// Finds the nearest uninherited timing point which starts after a given time. /// </summary> /// <param name="time"></param> /// <returns></returns> public TimingPoint GetRedlineAfterTime(double time) { return(TimingPoints.FirstOrDefault(tp => Precision.DefinitelyBigger(tp.Offset, time) && tp.Uninherited)); }
private void updatePopoverPositioning() { if (target == null || currentPopover == null) { return; } var targetLocalQuad = ToLocalSpace(target.ScreenSpaceDrawQuad); Anchor bestAnchor = Anchor.Centre; float biggestArea = 0; float totalSize = Math.Max(DrawSize.X * DrawSize.Y, 1); foreach (var anchor in candidate_anchors) { // Compute how much free space is available on this side of the target. var availableSize = availableSizeAroundTargetForAnchor(targetLocalQuad, anchor); float area = availableSize.X * availableSize.Y / totalSize; // If the free space is insufficient for the popover to fit in, do not consider this anchor further. if (availableSize.X < currentPopover.BoundingBoxContainer.DrawWidth || availableSize.Y < currentPopover.BoundingBoxContainer.DrawHeight) { continue; } // The heuristic used to find the "best" anchor is the biggest area of free space available in the popover container // on the side of the anchor. if (Precision.DefinitelyBigger(area, biggestArea, 0.01f)) { biggestArea = area; bestAnchor = anchor; } } currentPopover.PopoverAnchor = bestAnchor.Opposite(); var positionOnQuad = bestAnchor.PositionOnQuad(targetLocalQuad); currentPopover.Position = new Vector2(positionOnQuad.X - Padding.Left, positionOnQuad.Y - Padding.Top); // While the side has been chosen to maximise the area of free space available, that doesn't mean that the popover's body // will still fit in its entirety in the default configuration. // To avoid this, offset the popover so that it fits in the bounds of this container. var adjustment = new Vector2(); var popoverContentLocalQuad = ToLocalSpace(currentPopover.Body.ScreenSpaceDrawQuad); if (popoverContentLocalQuad.TopLeft.X < 0) { adjustment.X = -popoverContentLocalQuad.TopLeft.X; } else if (popoverContentLocalQuad.BottomRight.X > DrawWidth) { adjustment.X = DrawWidth - popoverContentLocalQuad.BottomRight.X; } if (popoverContentLocalQuad.TopLeft.Y < 0) { adjustment.Y = -popoverContentLocalQuad.TopLeft.Y; } else if (popoverContentLocalQuad.BottomRight.Y > DrawHeight) { adjustment.Y = DrawHeight - popoverContentLocalQuad.BottomRight.Y; } currentPopover.Position += adjustment; // Even if the popover was moved, the arrow should stay fixed in place and point at the target's centre. // In such a case, apply a counter-adjustment to the arrow position. // The reason why just the body isn't moved is that the popover's autosize does not play well with that // (setting X/Y on the body can lead BoundingBox to be larger than it actually needs to be, causing 1-frame-errors) currentPopover.Arrow.Position = -adjustment; }
static bool checkScrollCurrent(BasicScrollContainer scrolled, BasicScrollContainer notScrolled) => notScrolled.Current == 0 && Precision.DefinitelyBigger(scrolled.Current, 0f);