How do I scale one rectangle to the maximum size possible within another rectangle?
Asked Answered
E

5

77

I have a source rectangle and a destination rectangle. I need to find the maximum scale to which the source can be scaled while fitting within the destination rectangle and maintaining its original aspect ratio.

Google found one way to do it but I'm not sure if it works in all cases. Here is my home-brewed solution:

  • Calculate Height/Width for each rectangle. This gives the slopes of the diagonals msrc and mdest.
  • If msrc < mdst, scale source width to fit the destination width (and scale height by the same ratio)
  • Otherwise, scale source height to fit the destination height (and scale width by the same ratio)

Looking for other possible solutions to this problem. I'm not even sure if my algorithm works in all cases!

Egocentrism answered 3/9, 2009 at 12:11 Comment(0)
S
158
scale = min(dst.width/src.width, dst.height/src.height)

This is your approach but written more cleanly.

To use this, the scaled rectangle would have the shape:

width = src.width * scale
height = src.height * scale
Scrutator answered 3/9, 2009 at 12:11 Comment(3)
Sweet! Once you have the scale, use these to get the final dimensions: width = src.width * scale and height = src.height * scaleJann
Change min with max if you want to cover whole destination area.Seabrook
Same solution, but with names more clear to me: scale = min(maxWidth/actualWidth, maxHeight/actualHeight), newWidth = actualWidth*scale, newHeight = actualHeight*scale.Surgeonfish
S
13

Another option might be to scale to maximum width and then check if the scaled height is greater then the maximum allowed height and if so scale by height (or vice versa):

scale = (dst.width / src.width);
if (src.height * scale > dst.height)
 scale = dst.height / src.height;

I think this solution is both shorter, faster and easier to understand.

Saudra answered 3/9, 2009 at 12:18 Comment(1)
I think you've got the ratio flipped in your third line.Scrutator
F
2
  1. Work out the smaller of destWidth / srcWidth and destHeight / srcHeight
  2. Scale by that

edit it's of course the same as your method, with the pieces of the formula moved around. My opinion is that this is clearer semantically, but it's only that - an opinion.

Fane answered 3/9, 2009 at 12:14 Comment(0)
C
1

If all dimensions are non-zero, I would use the following code (that essentially matches your code).

scaleFactor = (outerWidth / outerHeight > innerWidth / innerHeight) 
    ? outerHeight / innerHeight
    : outerWidth / innerWidth

This can also be modified to allow any dimension to be zero if required.

Capital answered 3/9, 2009 at 12:11 Comment(4)
I think a rectangle with a zero dimension is called a "line". :PSplurge
This solution is mathematically identical to mine: multiply your inequality by (innerHeight * outerHeight / innerWidth) and you get my inequality. The advantage of my code is that if the inequality fails, then the solution need not be recomputed.Saudra
The ternariy operator will evaluate the condition only once, too. And, of course, all solutions should be mathematical equivalent ... or wrong ...Darius
The ternary operator will evaluate the condition only once, but by formatting the condition differently part of the calculation of the condition can be one of the answers. Notice that you have always 3 multiplication operations in the computation. My solution has 3 operations in the worst case, otherwise it has only 2 because the correct calculation was already done before the test. Hence, 1/6th better performance (assuming random distribution of values).Saudra
W
0

The other answers suffer from a risk of generating a division by zero exception when either the sourceWidth or sourceHeight becomes zero. To safeguard against this, we should rewrite the comparison into a mathematically equivalent multiple expression. Also, additional edge condition to catch the infinite scale scenario.

Apart from having the scale, I really wanted the dimensions of the target rectangle, so, here I will provide the scale calculation and the target rectangle calculation.

Because of the infinity edge condition, I think the target rectangle will be more robust / useful:

    if (sourceWidth == 0 && sourceHeight == 0) {
        // scale = Infinity;
        outputWidth = 0;
        outputHeight = 0;
        outputX = destWidth / 2;
        outputY = destHeight / 2;
    } else if (destWidth * sourceHeight > destHeight * sourceWidth) {
        scale = destHeight / sourceHeight;
        outputWidth = sourceWidth * destHeight / sourceHeight;
        outputHeight = destHeight;
        outputX = (destWidth - outputWidth) / 2;
        outputY = 0;
    } else {
        scale = destWidth / sourceWidth;
        outputWidth = destWidth;
        outputHeight = sourceHeight * destWidth / sourceWidth;
        outputX = 0;
        outputY = (destHeight - outputHeight) / 2;
    }
Winsome answered 3/9, 2009 at 12:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.