如何生成彼此不相交的方块(随机定位,大小相等,随机旋转)?

时间:2017-09-06 17:52:06

标签: python python-2.7 python-3.x

我一直致力于在1x1网格上生成一层随机旋转并放置的正方形。我已经能够生成一个随机放置并在网格上旋转的单个方块,但我不确定如何改进代码以生成更多不相互交叉的随机方块。目前的代码见下文:

Example of my One Randomized Square

from math import cos, pi, sin
from random import randint
from matplotlib.mlab import frange
from matplotlib.pyplot import plot, axis, show

def flake_position_layer1(): #Determines the initial position of one corner of the square
    x0 = randint(0, 100) / 100
    y0 = randint(0, 100) / 100
    theta = randint(0, 90) * pi / 180 #Angle of rotation for the square
    return x0, y0, theta


def flake_shape(): #generates the other 3 corners of the square
    x0, y0, z, theta = flake_position_layer1()
    x1 = x0 + (0.1 * cos(theta))
    x2 = x1 + (0.1 * cos((90 * pi/180) + theta))
    x3 = x2 + (0.1 * cos((180 * pi/180) + theta))
    y1 = y0 + (0.1 * sin(theta))
    y2 = y1 + (0.1 * sin((90 * pi/180) + theta))
    y3 = y2 + (0.1 * sin((180 * pi/180) + theta))
    return x0, x1, x2, x3, y0, y1, y2, y3


def display(): #connects the 4 corners on a plot
    x0, x1, x2, x3, y0, y1, y2, y3 = flake_shape()
    return plot([x0, x1, x2, x3, x0], [y0, y1, y2, y3, y0])


display()
axis([0,1,0,1]) #1x1 grid
show()

我没有CS背景(我是环境工程专业),而且我对编码非常缺乏经验。请给我任何建议,让我尝试解决这个问题!

2 个答案:

答案 0 :(得分:1)

您需要一个函数来确定两个立方体是否相交。

--http-keepalive

答案 1 :(得分:1)

好的,这是我在shapely软件包的帮助下提出的。安装帮助在这里的底部。最终结果:

enter image description here enter image description here

代码演练

  • distance是一个辅助函数,它使用Point类从形状上找到两个坐标之间的距离。只是一个辅助功能,以后再用。
  • Square实例化一个新的多边形。它有4个角,每个角为(x,y)对,其中心为一个坐标,标量值等于对角线距离的一半。
  • test_overlap通过标题非常自我解释。但从逻辑上讲,它的作用是:找到两个形状之间从中心到中心的距离。然后找到每个形状的半对角线的总和。如果中心距离大于总和,则方格不能重叠。
  • Squares从一个空容器(空列表)开始,并尝试向其添加方块。但是对于每一个可能的新增加,它首先测试与现有的正方形没有重叠。

代码

import math
import random

from shapely.geometry import Polygon, Point


def distance(a, b):
    return Point(a).distance(Point(b))


class Square(object):
    def __init__(self):
        self.x0, self.y0 = random.random(), random.random()
        theta = random.randint(0, 90) * math.pi / 180  # Angle of rotation
        self.x1 = self.x0 + (0.1 * math.cos(theta))
        self.x2 = self.x1 + (0.1 * math.cos((90 * math.pi/180) + theta))
        self.x3 = self.x2 + (0.1 * math.cos((180 * math.pi/180) + theta))
        self.y1 = self.y0 + (0.1 * math.sin(theta))
        self.y2 = self.y1 + (0.1 * math.sin((90 * math.pi/180) + theta))
        self.y3 = self.y2 + (0.1 * math.sin((180 * math.pi/180) + theta))
        self.corners = ((self.x0, self.y0), (self.x1, self.y1), 
                        (self.x2, self.y2), (self.x3, self.y3))

    @property
    def center(self):
        """(x, y) of the center of the polygon."""
        return Polygon(self.corners).centroid.coords[0]

    @property
    def half_diag(self):
        """The distance of 1/2 the shape's diagonal (center-to-corner)."""
        p0, p1, p2, p3 = self.corners
        return 0.5 * distance(p0, p1) * math.sqrt(2)


def test_overlap(square1, square2):
    """Do two shapes overlap?  

    Note this is a 'conservative' test.  May return True if they do not
    (false positive), but will never return False if they do (false negative).
    """

    # Distance between two centers
    ctc = distance(square1.center, square2.center)
    # Sum of half-diagonals
    halfdiags = square1.half_diag + square2.half_diag
    res = ctc < halfdiags
    return res


class Squares(object):
    def __init__(self):
        self.squares = []

    def add_square(self):
        new_square = Square()
        if not self.squares:
            # Initial empty list/container - just add without any tests
            self.squares.append(new_square)
        else:
            while True:
            # Test that new_square overlaps with existing
                res = [test_overlap(square, new_square) for square in self.squares]
                if any(res):
                    # We have at least 1 case of overlap (1 True)
                    new_square = Square()
                else:
                    # Safe to add
                    self.squares.append(new_square)
                    break

    def plot_squares(self):
        for square in self.squares:
            (x0, y0), (x1, y1), (x2, y2), (x3, y3) = square.corners
            plt.plot([x0, x1, x2, x3, x0], [y0, y1, y2, y3, y0])  

实施例

import itertools
%matplotlib inline
for _ in itertools.repeat(None, 10):
    # Add 10 squares; you could also just build this into the class
    sqs.add_square()
sqs.plot_squares()

enter image description here

正在安装shapely

如果您还没有安装Anaconda发行版。然后只需使用conda-forge进行整形安装即可。从cmd运行:

conda config --add channels conda-forge
conda install shapely

明显不足

在某个时刻,你的方块容器被填满,剩下的空间很小,无法添加新的形状。即使有可用空间,该功能基本上是反复试验,因此在高形状计数时需要很长时间。目前这种情况发生在大约20-25个方格(1.0x1.0方框)中。