private void scaleSlider(Slider slider, Vector2 scale) { referencePathTypes ??= slider.Path.ControlPoints.Select(p => p.Type.Value).ToList(); Quad sliderQuad = GetSurroundingQuad(slider.Path.ControlPoints.Select(p => p.Position.Value)); // Limit minimum distance between control points after scaling to almost 0. Less than 0 causes the slider to flip, exactly 0 causes a crash through division by 0. scale = Vector2.ComponentMax(new Vector2(Precision.FLOAT_EPSILON), sliderQuad.Size + scale) - sliderQuad.Size; Vector2 pathRelativeDeltaScale = new Vector2( sliderQuad.Width == 0 ? 0 : 1 + scale.X / sliderQuad.Width, sliderQuad.Height == 0 ? 0 : 1 + scale.Y / sliderQuad.Height); Queue <Vector2> oldControlPoints = new Queue <Vector2>(); foreach (var point in slider.Path.ControlPoints) { oldControlPoints.Enqueue(point.Position.Value); point.Position.Value *= pathRelativeDeltaScale; } // Maintain the path types in case they were defaulted to bezier at some point during scaling for (int i = 0; i < slider.Path.ControlPoints.Count; ++i) { slider.Path.ControlPoints[i].Type.Value = referencePathTypes[i]; } //if sliderhead or sliderend end up outside playfield, revert scaling. Quad scaledQuad = getSurroundingQuad(new OsuHitObject[] { slider }); (bool xInBounds, bool yInBounds) = isQuadInBounds(scaledQuad); if (xInBounds && yInBounds && slider.Path.HasValidLength) { return; } foreach (var point in slider.Path.ControlPoints) { point.Position.Value = oldControlPoints.Dequeue(); } }
/// <summary> /// Clamp scale for multi-object-scaling where selection does not exceed playfield bounds or flip. /// </summary> /// <param name="hitObjects">The hitobjects to be scaled</param> /// <param name="reference">The anchor from which the scale operation is performed</param> /// <param name="scale">The scale to be clamped</param> /// <returns>The clamped scale vector</returns> private Vector2 getClampedScale(OsuHitObject[] hitObjects, Anchor reference, Vector2 scale) { float xOffset = ((reference & Anchor.x0) > 0) ? -scale.X : 0; float yOffset = ((reference & Anchor.y0) > 0) ? -scale.Y : 0; Quad selectionQuad = getSurroundingQuad(hitObjects); //todo: this is not always correct for selections involving sliders. This approximation assumes each point is scaled independently, but sliderends move with the sliderhead. Quad scaledQuad = new Quad(selectionQuad.TopLeft.X + xOffset, selectionQuad.TopLeft.Y + yOffset, selectionQuad.Width + scale.X, selectionQuad.Height + scale.Y); //max Size -> playfield bounds if (scaledQuad.TopLeft.X < 0) { scale.X += scaledQuad.TopLeft.X; } if (scaledQuad.TopLeft.Y < 0) { scale.Y += scaledQuad.TopLeft.Y; } if (scaledQuad.BottomRight.X > DrawWidth) { scale.X -= scaledQuad.BottomRight.X - DrawWidth; } if (scaledQuad.BottomRight.Y > DrawHeight) { scale.Y -= scaledQuad.BottomRight.Y - DrawHeight; } //min Size -> almost 0. Less than 0 causes the quad to flip, exactly 0 causes scaling to get stuck at minimum scale. Vector2 scaledSize = selectionQuad.Size + scale; Vector2 minSize = new Vector2(Precision.FLOAT_EPSILON); scale = Vector2.ComponentMax(minSize, scaledSize) - selectionQuad.Size; return(scale); }