简体   繁体   English

画布中最佳正方形大小的算法

[英]Algorithm for Optimal Square Size in Canvas

I'm looking to create a grid of equally-sized squares whose size are based on the area of the canvas.我希望创建一个大小相等的正方形网格,其大小基于画布的面积。 I'd like the squares to take the maximum area possible in the container.我希望正方形占据容器中可能的最大面积。

I know I can find the area of the canvas by:我知道我可以通过以下方式找到画布的区域:

area = canvas.width * canvas.height; // e.g. 640x360 = 230400
areaPerSquare = parseInt(area/squareCount); // e.g. with a shape count of 4, 28800

If I divide that by the number of squares, I get a result, but I'm unsure what to do next.如果我将其除以平方数,就会得到结果,但我不确定下一步该怎么做。 Maybe this isn't the way to go about it...也许这不是解决问题的方法......

Here is where I am with the code.这是我使用代码的地方。 I've brute forced the square size and it works for the first three values, but I'm not able sure how to maximize the size when more squares can be fit in subsequent rows.我已经强制使用正方形大小,它适用于前三个值,但是当更多正方形可以放入后续行时,我无法确定如何最大化大小。 How can I determine the optimal square size?如何确定最佳正方形大小? Thank you!谢谢!

EDIT: There can be empty space as all squares should be the same, maximum size.编辑:可以有空白空间,因为所有方块都应该是相同的,最大尺寸。

 var canvas = document.querySelector("#canvas"); var ctx = canvas.getContext("2d"); var squareCountNum = document.querySelector("#squareCount"); var widthNum = document.querySelector("#width"); var heightNum = document.querySelector("#height"); var squareCount = squareCountNum.value; squareCountNum.onchange = function(e) { squareCount = e.target.value; draw(); } widthNum.onchange = function(e) { canvas.width = e.target.value; canvas.style.width = e.target.value + "px"; draw(); } heightNum.onchange = function(e) { canvas.height = e.target.value; canvas.style.height = e.target.value + "px"; draw(); } function draw() { ctx.clearRect(0,0,canvas.width,canvas.height); let area = canvas.width * canvas.height; let areaPerSquare = parseInt(area/squareCount); let size = 100; if (squareCount == 1) { if (canvas.width > canvas.height) { size = canvas.height; } else { size = canvas.width; } } else { if (canvas.width > canvas.height) { size = canvas.width/squareCount; } else { size = canvas.height/squareCount; } } let row = 0; let currentIndex = 0; for (let i=0; i<squareCount; i++) { ctx.beginPath(); if (currentIndex*size+size > canvas.width) { row++; currentIndex = 0; } ctx.rect(currentIndex*size, row*size, size, size); //how do I maximize size? ctx.fillStyle = "#"+Math.floor(Math.random()*16777215).toString(16); ctx.fill(); ctx.closePath(); currentIndex++; } } draw();
 #canvas { width: 320px; height: 180px; border: 1px solid rgba(0,0,0,0.2); } #squareCount { margin-bottom: 20px; }
 <canvas id="canvas" width="640" height="360"></canvas> <div><label>Square Count</label><input type="number" id="squareCount" value=1></input></div> <div><label>Width</label><input type="number" id="width" value=320></input></div> <div><label>Height</label><input type="number" id="height" value=180></input></div>

Here is an illustration of the desired result of the program with square count values 1-5:这是程序的期望结果的说明,其中平方计数值为 1-5: 在此处输入图片说明

Interesting problem, which is more math related than programming related...有趣的问题,与数学相关的比与编程相关的更多......

The crux of the problem is determining the appropriate number of rows and columns of squares that maximize area utilization.问题的关键是确定合适的正方形行数和列数,以最大限度地利用面积。 Towards that end, the algorithm below performs the following steps, using 19 squares in a 100 x 50 region as an example...为此,下面的算法执行以下步骤,以 100 x 50 区域中的 19 个方块为例......

  • Determine the maximum possible size of the square.确定正方形的最大可能尺寸。 This is done by taking the total area of the region and dividing by the number of required squares.这是通过取该区域的总面积并除以所需正方形的数量来完成的。 Eg, sqrt( 100 x 50 / 19 ) or 16.222 .例如, sqrt( 100 x 50 / 19 )16.222 That is, if you could use the total area of 5000, and 19 squares fit perfectly into that area, then the squares will be 16.222 x 16.222 in dimension.也就是说,如果您可以使用 5000 的总面积,并且 19 个正方形完全适合该区域,那么正方形的尺寸将是 16.222 x 16.222。
  • Determine the number of times that the maximum possible square will fit into the region's width and height.确定最大可能的正方形适合区域宽度和高度的次数。 Since this will very likely not be an integral number of times, we must therefore take the possible range of times.由于这很可能不是整数次,因此我们必须取可能的时间范围。 Eg, the maximum possible square can fit into the width 100 / 16.222 or 6.1644 times.例如,最大可能的正方形可以适应宽度的100 / 16.2226.1644倍。 Thus, the optimal number of possible cols will range from 6 to 7 columns.因此,可能的 cols 的最佳数量范围为 6 到 7 列。 Likewise, the fit for the height is 50 / 16.222 or 3.0822 times for a possible optimal row range of 3 to 4 rows.同样,对于 3 到 4 行的可能最佳行范围,高度的拟合是50 / 16.2223.0822倍。
  • Then, the algorithm simply loops through the possible combinations of rows and cols, ensuring that the number of rows * cols is greater than or equal to the number of squares.然后,算法简单地循环遍历行和列的可能组合,确保rows * colsrows * cols大于或等于平方数。 Eg, 6 * 3 is only 18, so is not a valid option, whereas 6 * 4 , 7 * 3 , and 7 * 4 are all greater than 19, so qualify as candidates.例如, 6 * 3只有 18,因此不是有效选项,而6 * 47 * 37 * 4都大于 19,因此有资格作为候选。
  • As it is, the algorithm returns all these candidates with the row and col counts, along with the dimension of the square and the calculated use of space.实际上,该算法返回所有这些候选者的行数和列数,以及正方形的尺寸和计算出的空间使用情况。 The code sample shows a means of extracting the maximum area utilization using the reduce function.代码示例展示了一种使用reduce函数提取最大区域利用率的方法。

 function maximizeSquaresInRegion( squareCount, width, height ) { // // Calculate the maximum possible side of the square, assuming full // use of the area of the region. // let maxSqrSide = Math.sqrt( ( width * height ) / squareCount ); // // Using the maximum possible side of the square, let's determine the // low end and high end number of times it can squeeze into a particular // dimension. // let rowsLo = Math.floor( height / maxSqrSide ); let rowsHi = rowsLo + 1; let colsLo = Math.floor( width / maxSqrSide ); let colsHi = colsLo + 1; // // Okay, now that we have the possible range of number of times that // the square can fit into each dimension, let's loop through the // combinations, finding all that meet the required squareCount and // calculating the area used. // let options = []; for ( let row = rowsLo; row <= rowsHi; row++ ) { for ( let col = colsLo; col <= colsHi; col++ ) { if ( squareCount <= row * col ) { // // First thing, find the minimum length of the side depending // on the fit by width or height. // let squareSideLen = Math.min( width / col, height / row ); let area = Math.min( row * col, squareCount ) * squareSideLen * squareSideLen; options.push( { rows: row, cols: col, side: squareSideLen, area: area } ); } } } return options; } console.log( 'Size: 100 x 50' ); console.log( 'Squares 4: ' ); console.log( maximizeSquaresInRegion( 4, 100, 50) ); console.log( 'Squares 17: ' ); console.log( maximizeSquaresInRegion( 17, 100, 50) ); console.log( 'Squares 19: ' ); console.log( maximizeSquaresInRegion( 19, 100, 50) ); console.log( 'Squares 94: ' ); console.log( maximizeSquaresInRegion( 94, 100, 50) ); console.log( 'Squares 19 Best Fit: ' ); console.log( maximizeSquaresInRegion( 19, 100, 50).reduce(( bf, opt ) => bf.area > opt.area ? bf : opt) );

EDIT : Based on anomaly whereby maximum possible square size fits perfectly into the regions width or height, the resulting range being checked is only one value for that dimension.编辑:基于异常情况,最大可能的正方形大小完全适合区域宽度或高度,所检查的结果范围仅为该尺寸的一个值。 Eg, a region of 320 x 180 with a fit for 9 squares results in a maximum possible square size of 80 x 80, which goes evenly into 320 by 4 times.例如,适合 9 个正方形的 320 x 180 区域导致最大可能的正方形大小为 80 x 80,它均匀地变成 320 乘以 4 倍。 This results in a range change for the width of only 4 to 4 columns.这导致仅 4 到 4 列的宽度范围发生变化。 The adjustment to the code makes this check 4 to 5 columns.对代码的调整使此检查有 4 到 5 列。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM