在保持长宽比的情况下,将N个矩形适应于给定区域

5

我有N个矩形,它们的尺寸都相同为rectWidth*rectHeight

我有一个区域,其尺寸为areaWidth*areaHeight

我想将这N个矩形放入该区域内,保持矩形的纵横比,调整矩形大小以使其适合区域内。

在矩形之间,我希望有一个间距为space

如何确定矩形的尺寸,以便它们都适应矩形内并保持其纵横比?


2
你需要更明确地指定这个问题。现在这个问题非常不确定:我可以将所有矩形调整为0x0的大小。你想要特定的放置方式吗?它们是否都应该按相同的比例调整大小? - Thomas
嗨,Thomas,感谢你的回复。放置不是问题,我将矩形传递给控件进行放置。所有N个矩形具有相同的尺寸,在新情况下必须保持相同的尺寸。我希望它们保持其原始大小(rectWidth * rectHeight),或者如果它们无法全部适应,则更小。算法的结果应该是调整大小的因子,以便它们适合。如果这个因子> 1,我会保留它的大小,如果这个因子<1,我将按该因子调整所有矩形的大小。我希望这足够清楚地解释了要求和限制。 - Serge van den Oever
我认为这个问题应该重新表述为:矩形的最大尺寸是多少,以便将它们全部放入一个矩形中并保持纵横比? - 31415926
我尝试了一种直接的公式方法,这种方法告诉我a应该接近于NR/r,b应该接近于Nr/R,其中R是您区域的长宽比,r和N分别是矩形的长宽比和数量,而a和b分别是区域宽度和高度中的矩形数量。在考虑间距之前,有人能否找到使用此http://jsfiddle.net/ohbhy4uw/会导致错误结果的配置吗? - Cimbali
2个回答

5
让有N个矩形。
让矩形的尺寸为(cw,ch),其中0 < c ≤ 1。
让您要适合的区域大小为(W,H)。
让s ≥ 0为矩形间隙。
水平放置的a > 0个矩形的水平大小为acw + (a - 1)s。
我们知道acw + (a - 1)s ≤ W。
垂直放置的b > 0个矩形的垂直大小为bch + (b - 1)s。
我们知道bch + (b - 1)s ≤ H。
然后我们有以下优化问题。
最大c
限制条件
a ≤ (W + s) / (cw + s)
b ≤ (H + s) / (ch + s)
ab ≥ N
0 < c ≤ 1
a,b > 0和整数
现在考虑以下不等式。
a ≤ (W + s) / (cw + s)
b ≤ (H + s) / (ch + s)
任何最优解必须使至少一个这些成为紧密不等式。
也就是说,对于最优解(a,b,c)中至少一项,以下至少之一成立。
a = (W + s) / (cw + s) ↔ c = (W - s(a - 1)) / wa
b = (H + s) / (ch + s) ↔ c = (H - s(b - 1)) / wb
假设不失一般性,a = (W + s) / (cw + s)成立。
由于a必须取{1,2,...,N}中的一个值,
c必须从以下值之一中取出{W/w,(W-s)/2w,(W-2s)/3w,...,(W-(N-1)s)/Nw}。
类似的推理给出了在第二个不等式(对于b)紧密的情况下必须从中取出值的列表。
如果合并这两个值列表,则最优解中c可以取的最多有2N个值。 对这个列表中存在可行的a和b的最大c进行排序,然后进行二进制搜索。
检查c值是否可行的方法是设置:
a = floor((W + s) / (cw + s))
b = floor((H + s) / (ch + s))
然后检查ab≥N。

这看起来很棒...但需要研究一下:-) 我们(Pi=31415926 & I)将尝试将其转换为JavaScript函数:-) - Serge van den Oever
你能具体一点吗? - 31415926
我点了踩,因为我认为算法由于2N个循环和假设0 < c ≤ 1而变得不够高效。如果你的w,h比适配矩形的尺寸小,那么c将会大于1。此外,你需要检查c的值是否可行...这是一个额外的循环。当你编辑好后,我会给你点赞的。 - 31415926
1
@31415926从问题的评论中可以看出:“我希望它们保持自己的原始大小(rectWidth * rectHeight),或者如果它们不能全部适合,则变小。”你对批评有点过分苛刻了,所以我不想再讨论了。 - Timothy Shields
1
@31415926 你的算法和这个算法几乎完全相同,除了假设 c <= 1 和在第一个循环中检查可行性(即没有“水槽”以外的空闲空间)。你们都假设一个方向将被填充(除了“水槽”以外没有空闲空间),在每个方向上迭代所有可能的矩形数量,取最大可行值,然后取两个方向的最大结果。无论你像 Timothy 一样做两个 O(n) 循环和一个 O(2n) 循环,还是像你一样做两个 O(n) 循环但指令数翻倍,都是严格相同的。 - Cimbali
显示剩余6条评论

2
这个JavaScript解决方案怎么样?
var areaHeight = window.innerHeight;   //set here your area height
var areaWidth = window.innerWidth;     //set here your area width
var N = 216;                           //set amount of rectangles you want to fit
var rectRatio = 9/4;                   //set rectangle ratio
var gutter = [5, 10];                  //set x and y spacing between rectangles
var cols, rows, rectHeight, rectWidth; //variables that we need to calculate

该函数假设矩形网格(画布)始终适合容器区域的高度。您将行数提供给函数,它会计算矩形大小并确定画布宽度是否大于容器宽度。如果画布更大,则会增加行数并再次调用该函数。

function rowIterator(iterator) {
   rows = iterator;
   cols = Math.ceil(N/rows);  

   rectHeight = (areaHeight - (rows-1)*gutter[1])/rows;          
   rectWidth = rectHeight*rectRatio;

   if (cols * rectWidth + (cols - 1)*gutter[0] > areaWidth) {
       rowIterator(rows + 1);
   }
}

rowIterator(1);                       //feed initial value
var size1 = [rectWidth, rectHeight];

如果您也关心查找最大矩形大小,而不仅仅是适合它,那么迭代还应该针对列进行,并选择更大的矩形大小:

function colIterator(iterator) {
   cols = iterator;
   rows = Math.ceil(N/cols);

   rectWidth = (areaWidth - (cols - 1)*gutter[0])/cols;
   rectHeight = rectWidth/rectRatio;

   if (rows * rectHeight + (rows - 1)*gutter[1] > areaHeight) {
       colIterator(cols + 1);
   }
}
colIterator(1);
var size2 = [rectWidth, rectHeight];

两个迭代器的总迭代次数大约为N,最大矩形大小为:

optimalRectSize = [Math.max(size1[0], size2[0]), Math.max(size1[1], size2[1])]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接