Fix Inconsistent Scale Behavior when Scaling Objects with Shift Modifier (#300)

* Fix Objects Growing near Infinity when Scaling w Aspect Ratio Maintained
* Refactor adjustCoordsForRationalScale to Get Rid of Code Duplication and Improve Comprehensibility
* Rename getNewSizeForRationalScale to GetNewSizeForRationalScale for Consistency
This commit is contained in:
jklingen 2021-03-27 19:54:31 +01:00 committed by GitHub
commit 4a959621ff
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -192,62 +192,83 @@ namespace Greenshot.Helpers {
}
/// <summary>
/// Adjusts resizeHandleCoords so that aspect ratio is kept after resizing a given rectangle with provided arguments
/// Adjusts resizeHandleCoords so that aspect ratio is kept after resizing a given rectangle with provided arguments.
/// An adjustment can always be done in two ways, e.g. *in*crease width until fit or *de*crease height until fit.
/// To avoid objects growing near infinity unexpectedly in certain combinations, the adjustment will choose the
/// option resulting in the smaller rectangle.
/// </summary>
/// <param name="originalRectangle">bounds of the current rectangle</param>
/// <param name="resizeHandlePosition">position of the handle/gripper being used for resized, see Position</param>
/// <param name="resizeHandleCoords">coordinates of the used handle/gripper, adjusted coordinates will be written to this reference</param>
private static void AdjustCoordsForRationalScale(RectangleF originalRectangle, Positions resizeHandlePosition, ref PointF resizeHandleCoords) {
float originalRatio = originalRectangle.Width / originalRectangle.Height;
float newWidth, newHeight, newRatio;
SizeF selectedRectangle, newSize;
switch(resizeHandlePosition) {
case Positions.TopLeft:
newWidth = originalRectangle.Right - resizeHandleCoords.X;
newHeight = originalRectangle.Bottom - resizeHandleCoords.Y;
newRatio = newWidth / newHeight;
if(newRatio > originalRatio) { // FIXME
resizeHandleCoords.X = originalRectangle.Right - newHeight * originalRatio;
} else if(newRatio < originalRatio) {
resizeHandleCoords.Y = originalRectangle.Bottom - newWidth / originalRatio;
}
selectedRectangle = new SizeF(originalRectangle.Right - resizeHandleCoords.X, originalRectangle.Bottom - resizeHandleCoords.Y);
newSize = GetNewSizeForRationalScale(originalRectangle.Size, selectedRectangle);
resizeHandleCoords.X = originalRectangle.Right - newSize.Width;
resizeHandleCoords.Y = originalRectangle.Bottom - newSize.Height;
break;
case Positions.TopRight:
newWidth = resizeHandleCoords.X - originalRectangle.Left;
newHeight = originalRectangle.Bottom - resizeHandleCoords.Y;
newRatio = newWidth / newHeight;
if(newRatio > originalRatio) { // FIXME
resizeHandleCoords.X = newHeight * originalRatio + originalRectangle.Left;
} else if(newRatio < originalRatio) {
resizeHandleCoords.Y = originalRectangle.Bottom - newWidth / originalRatio;
}
selectedRectangle = new SizeF(resizeHandleCoords.X - originalRectangle.Left, originalRectangle.Bottom - resizeHandleCoords.Y);
newSize = GetNewSizeForRationalScale(originalRectangle.Size, selectedRectangle);
resizeHandleCoords.X = originalRectangle.Left + newSize.Width;
resizeHandleCoords.Y = originalRectangle.Bottom - newSize.Height;
break;
case Positions.BottomLeft:
newWidth = originalRectangle.Right - resizeHandleCoords.X;
newHeight = resizeHandleCoords.Y - originalRectangle.Top;
newRatio = newWidth / newHeight;
if(newRatio > originalRatio) {
resizeHandleCoords.X = originalRectangle.Right - newHeight * originalRatio;
} else if(newRatio < originalRatio) {
resizeHandleCoords.Y = newWidth / originalRatio + originalRectangle.Top;
}
selectedRectangle = new SizeF(originalRectangle.Right - resizeHandleCoords.X, resizeHandleCoords.Y - originalRectangle.Top);
newSize = GetNewSizeForRationalScale(originalRectangle.Size, selectedRectangle);
resizeHandleCoords.X = originalRectangle.Right - newSize.Width;
resizeHandleCoords.Y = originalRectangle.Top + newSize.Height;
break;
case Positions.BottomRight:
newWidth = resizeHandleCoords.X - originalRectangle.Left;
newHeight = resizeHandleCoords.Y - originalRectangle.Top;
newRatio = newWidth / newHeight;
if(newRatio > originalRatio) {
resizeHandleCoords.X = newHeight * originalRatio + originalRectangle.Left;
} else if(newRatio < originalRatio) {
resizeHandleCoords.Y = newWidth / originalRatio + originalRectangle.Top;
}
selectedRectangle = new SizeF(resizeHandleCoords.X - originalRectangle.Left, resizeHandleCoords.Y - originalRectangle.Top);
newSize = GetNewSizeForRationalScale(originalRectangle.Size, selectedRectangle);
resizeHandleCoords.X = originalRectangle.Left + newSize.Width;
resizeHandleCoords.Y = originalRectangle.Top + newSize.Height;
break;
}
}
/// <summary>
/// For an original size, and a selected size, returns the the largest possible size that
/// * has the same aspect ratio as the original
/// * fits into selected size
/// </summary>
/// <param name="originalSize">size to be considered for keeping aspect ratio</param>
/// <param name="selectedSize">selection size (i.e. the size we'd produce if we wouldn't keep aspect ratio)</param>
/// <returns></returns>
private static SizeF GetNewSizeForRationalScale(SizeF originalSize, SizeF selectedSize)
{
SizeF newSize = selectedSize;
float originalRatio = originalSize.Width / originalSize.Height;
float selectedRatio = selectedSize.Width / selectedSize.Height;
// will fix orientation if the scaling causes size to be flipped in any direction
int flippedRatioSign = Math.Sign(selectedRatio) * Math.Sign(originalRatio);
if (Math.Abs(selectedRatio) > Math.Abs(originalRatio))
{
// scaled rectangle (ratio) would be wider than original
// keep height and tweak width to maintain aspect ratio
newSize.Width = selectedSize.Height * originalRatio * flippedRatioSign;
}
else if (Math.Abs(selectedRatio) < Math.Abs(originalRatio))
{
// scaled rectangle (ratio) would be taller than original
// keep width and tweak height to maintain aspect ratio
newSize.Height = selectedSize.Width / originalRatio * flippedRatioSign;
}
return newSize;
}
public static void Scale(Rectangle boundsBeforeResize, int cursorX, int cursorY, ref RectangleF boundsAfterResize) {
Scale(boundsBeforeResize, cursorX, cursorY, ref boundsAfterResize, null);
}
public static void Scale(Rectangle boundsBeforeResize, int cursorX, int cursorY, ref RectangleF boundsAfterResize, IDoubleProcessor angleRoundBehavior) {
Scale(boundsBeforeResize, Positions.TopLeft, cursorX, cursorY, ref boundsAfterResize, angleRoundBehavior);