Search code examples
pythonrandomposition

How to generate random points outside a rectangle?


Let's say my window's dimensions are 400 width by 600 height.

It's relatively easy to generate a random point on a single side, let's say the top of it:

random.randint(0, width)

But what would be the smartest way to get this to work for all 4 sides so that a random point is generated outside a rectangle?

If I do

pos_x = [random.randint(0, width)]
pos_y = [random.randint(0, height)]

They will only show up on the corners, which makes sense. The only way I can think of is to randomly create a point inside the rectangle, compare what axis is closest to a border, then clamp it. Thing is I don't know how to do this elegantly without doing 4 checks for each side (which feels redundant). I feel like there is an easier solution for this?

Here's a solution that almost works but it's so long winded. Just realized that this gets less points in the corners.

# Create a random point inside the rectangle
pos_x = random.randint(0, width)
pos_y = random.randint(0, height)

# Get a distance for each side
left_border = pos_x
right_border = width-pos_x
top_border = pos_y
bottom_border = height-pos_y

borders = [left_border, right_border, top_border, bottom_border]

index_1 = 0
index_2 = 2
closest_side = 0

# Get closest from left/right borders
if right_border < left_border:
    index_1 = 1

# Get closest from top/bottom borders
if bottom_border < top_border:
    index_2 = 3

# Get closest border
if borders[index_1] < borders[index_2]:
    closest_side = index_1
else:
    closest_side = index_2

if closest_side == 0:
    obj.pos.x = 0 # Clamp to left wall
elif closest_side == 1:
    obj.pos.x = width # Clamp to right wall
elif closest_side == 2:
    obj.pos.y = 0 # Clamp to top wall
else:
    obj.pos.y = height # Clamp to bottom wall

Solution

  • Sorry; completely misinterpreted the question the first time.

    If you just want to pick a point randomly on the edge of a rectangle, try something like this:

    p = random.randint(0, width + width + height + height)
    if p < (width + height):
      if p < width:
        obj.pos.x = p
        obj.pos.y = 0
      else:
        obj.pos.x = width
        obj.pos.y = p - width
    else:
      p = p - (width + height)
      if p < width:
        obj.pos.x = width - p
        obj.pos.y = height
      else:
        obj.pos.x = 0
        obj.pos.y = height - (p - width)
    

    Simply, it picks a random point on a line the same length as the perimeter of the rectangle, then piecewise wraps that line around the rectangle to give x and y coordinates.

    To ensure that the distribution stays uniform at the corners, each of the four segments are taken as inclusive-exclusive intervals (can be 0, cannot be width or height) and they're placed such that each one of them starts in a different corner.