在我的工作办公室里,我们不允许涂漆墙壁,因此我决定制作一些边框和矩形,将一些漂亮的织物固定在上面,并将它们排列在墙上。
我正在尝试编写一个方法,该方法将采用我的输入尺寸(9' x 8' 8")和最小/最大尺寸(1' x 3',2',4'等),并生成一个随机的方块和矩形图案来填充墙壁。 我曾尝试手动完成这个过程,但是我对得到的布局不满意,并且每次想要“随机化”布局时需要大约35分钟。
在我的工作办公室里,我们不允许涂漆墙壁,因此我决定制作一些边框和矩形,将一些漂亮的织物固定在上面,并将它们排列在墙上。
我正在尝试编写一个方法,该方法将采用我的输入尺寸(9' x 8' 8")和最小/最大尺寸(1' x 3',2',4'等),并生成一个随机的方块和矩形图案来填充墙壁。 我曾尝试手动完成这个过程,但是我对得到的布局不满意,并且每次想要“随机化”布局时需要大约35分钟。
一种解决方案是从 x*y 的正方形开始,随机合并这些正方形以形成矩形。您需要对不同大小的正方形赋予不同的权重,以防止算法最终只生成大量微小的矩形(例如,大型矩形应该具有更高的合并几率,直到它们变得太大为止)。
1. Randomly generate points on the wall
Use as many points as the number of rectangles you want
Introduce sampling bias to get cooler patterns
2. Build the kd-tree of these points
如果你在讨论纯编程问题 ;) 有一种叫做装箱问题的技术,它试图将多个垃圾桶尽可能地打包在最小的区域内。这方面有大量的资料:
http://en.wikipedia.org/wiki/Bin_packing_problem
http://mathworld.wolfram.com/Bin-PackingProblem.html
http://www.cs.sunysb.edu/~algorith/files/bin-packing.shtml
所以您可以创建一堆随机的正方形,并将其通过一个装箱程序来生成您的图案。
我自己没有实现过装箱算法,但我曾看到我的同事在Nike网站上实现了它。祝好运。
由于您可以选择矩形的大小,所以这不是一个难题。
我认为你可以做一些简单的事情:
选择一个当前不在矩形内的(x,y)坐标。 选择第二个(x,y)坐标,使得当您在两个坐标之间绘制矩形时,它不会重叠任何东西。有效点的边界框仅由最近矩形的墙壁限定。 绘制该矩形。 重复以上步骤,直到覆盖了90%的面积。此时,您可以停止或使用尽可能大的矩形填补剩余的空洞。
将点的生成参数化并进行遗传算法可能很有趣。适应度函数将是您喜欢排列的程度-它会为您绘制数百种排列,并且您会根据1-10的评分对它们进行评级。然后它会取出最好的那些并进行微调,重复此过程,直到获得您真正喜欢的排列。
在 Philippe Beaudoin 的答案基础上构建。
还有其他语言中的树状图实现,您也可以使用。在 Ruby 中,您可以使用 RubyTreeMap 进行操作:
require 'Treemap'
require 'Treemap/image_output.rb'
root = Treemap::Node.new 0.upto(100){|i| root.new_child(:size => rand) }
output = Treemap::ImageOutput.new do |o|
o.width = 800
o.height = 600
end
output.to_png(root, "C:/output/test.png")
然而它会对矩形进行排序,所以看起来并不是很随机,但这可能是一个开始。有关更多信息,请参见rubytreemap.rubyforge.org/docs/index.html
装箱还是方块装箱?
装箱: http://www.cs.sunysb.edu/~algorith/files/bin-packing.shtml
方块装箱: http://www.maa.org/editorial/mathgames/mathgames_12_01_03.html
如果你不介意重叠,这听起来更像是一个老派的随机方块绘画演示,类似于8位计算机时代。但如果你想特别极客一些,可以创建随机方块并解决装箱问题。
我会按照螺旋形慢慢生成所有内容。如果在任何时候,您发现您的解决方案被证明是“无法解决”的(即,无法在剩余的中间放置任何正方形以满足约束条件),请回到早期的草稿并更改一些正方形,直到找到一个满意的解决方案。
伪代码大致如下:
public Board GenerateSquares(direction, board, prevSquare)
{
Rectangle[] rs = generateAllPossibleNextRectangles(direction, prevSquare, board);
for(/*all possible next rectangles in some random order*/)){
if(board.add(rs[x]){
//see if you need to change direction)
Board nBoard = GenerateSquares(direction, board, rs[x]);
if(nBoard != null) return nBoard; //done
else board.remove(rs[x]);
}
}
//all possibilities tried, none worked
return null;
}
}
public double[] fillBoard(double width, double height, double maxside) {
double[] dest = new int[0];
double[] poly = new int[10];
poly[0] = 0; poly[1] = 0; poly[2] = width; poly[3] = 0;
poly[4] = width; poly[5] = height; poly[6] = 0; poly[7] = height;
poly[8] = 0; poly[9] = 0;
...
return dest; /* x,y pairs */
}
然后选择一个随机的顶点,找到线段最大距离的2倍之内的多边形线段。
找到所有垂直线的x值和所有水平线的y值。为每个x和y值的选择创建“好性质”的评分和用于生成两个坐标值之间的评分方程。优秀程度是通过减少剩余多边形中的线条数来衡量的。使用伪随机生成器为两个x坐标或两个y坐标之间的值范围生成三个选项。基于加权平均值的倾向于良好选项的评分和选择x对和y对值。将新矩形应用于列表中,通过从poly数组中剪切其形状并将矩形坐标添加到dest数组中来完成。
问题没有说明最小边长度参数。但如果需要,算法应该(在遇到间隙太小的问题时)不在选择列表中包含太小的候选项(它们偶尔会使它们为空),并取消选择一定半径内的周围矩形的数量,并执行该区域大小的新再生尝试,希望满足条件。如果较小的瓷砖的排列失败,则递归可以逐步删除更大的区域。
编辑
进行一些命中测试以消除潜在的重叠。开始打字前吃点菠菜;)
这种图形方法与Brian's answer有相似之处。