在 Processing 中,我如何编码固定数量的行,这些行半随机间隔并且仍然适合固定大小的图像?

How do I code, in Processing, a fixed number of lines that are semi-randomly spaced apart and still fit into a fixed-size image?

我对堆栈溢出和一般编程都不熟悉。我正在大学学习基础 python 课程,我很快就要交期末作业了。我决定尝试在 Processing 中以 Python 模式制作一些生成艺术。我的计划是通过使用不同长度、高度和颜色的线条来创建城市天际线的基本(抽象)图像,线条之间有一些半随机间距。我已经编写了一些能够在一定程度上做到这一点的代码。但是,我正在努力以一种遵循我在开始时定义的一些全局变量的方式来编写它。我确实可能得到 50 行,但它们并没有覆盖整个图像并且经常相互重叠,因此很难区分它们。另外,我想加入一些代码来指定建筑物的尽头,以便在图像的左侧和右侧获得空白 space。我尝试为此使用 building_start 和 building_end 变量,但显然我在建筑物之间添加分隔的方法已关闭。你能帮我解决这个问题吗?

提前致谢。代码及其输出如下所示。

import random as rd
w, h = 1000, 500
building_start = 25
building_end = 975
n_buildings = 50
building_sep = (building_end - building_start)/(n_buildings*1.2)
roof_style = [ROUND, SQUARE]

def setup():
    size(w,h)
    background(60, 60, 60)

building_x = building_start
building_y = 500

for i in range(n_buildings):
    building_x = building_sep * i 
    building_top = rd.randrange(50, 370, 10)
    
    line(building_x, building_y, building_x, building_y - building_length)
    stroke(rd.randint(200, 255), rd.randint(200, 255), rd.randint(200, 255))
    strokeWeight(rd.randrange(10, 28, 2))
    strokeCap(rd.choice(roof_style))

noFill()
rect(0, 0, 1000, 500)
stroke(255, 247, 247)

the output of the code

如果你希望没有重叠,你可以尝试实现这个算法:

另一个类似的算法:如果你需要 N 个宽度在 W 内的建筑,你可以建造 N*2-1 个总宽度为 W 的建筑,然后每隔一栋建筑移除一次.该算法的缺点:空间和建筑物的宽度大致相同。

使用 random.sample() 函数更容易实现:

import random

num_of_buildings = 5
total_width      = 100
max_height       = 50
min_height       = 10

x_coords = sorted(random.sample(range(0, total_width), num_of_buildings * 2))
print('x_coords:', x_coords)

heights = random.sample(range(min_height, max_height), num_of_buildings)
print('heights:', heights)

buildings = list(zip(x_coords[0::2], x_coords[1::2], heights))
print('buildings:', buildings)

for b in buildings:
    x = b[0]
    w = b[1] - b[0]
    h = b[2]
    print (f'Make the building: x = {x :2}, width = {w :2}, height = {h :2}')

输出:

x_coords: [2, 7, 9, 13, 21, 46, 47, 62, 79, 83]

heights: [35, 14, 28, 20, 12]

buildings: [(2, 7, 35), (9, 13, 14), (21, 46, 28), (47, 62, 20), (79, 83, 12)]

Make the building: x =  2, width =  5, height = 35
Make the building: x =  9, width =  4, height = 14
Make the building: x = 21, width = 25, height = 28
Make the building: x = 47, width = 15, height = 20
Make the building: x = 79, width =  4, height = 12

此实施方式的一个缺点是您很少会在您所在区域的最边缘获得建筑物。开始和结束几乎总是会有差距。它可以粗略地固定:只为第一座建筑 x = 0 和最后一座建筑 width = total_width - x。不过,这不是很好的解决方案。

https://docs.python.org/3/library/random.html


完整版本:

import random
from PIL import Image, ImageDraw

num_of_buildings = 30
total_width      = 800
max_height       = 400
min_height       = 100

im   = Image.new('RGB', (total_width, max_height), (255, 255, 255))
draw = ImageDraw.Draw(im)

x_coords  = sorted(random.sample(range(0, total_width), num_of_buildings * 2))
heights   = random.sample(range(min_height, max_height), num_of_buildings)
buildings = zip(x_coords[0::2], x_coords[1::2], heights)

for b in buildings:
    x1    = b[0]
    x2    = b[1]
    h     = max_height - b[2]
    color = (random.randint(128, 230), random.randint(128, 230), random.randint(128, 230))
    draw.rectangle((x1, max_height, x2, h), fill=color)

im.save('image.jpg')

输出:

对于建筑物的最大和最小宽度限制显然是绝望的。

好的,这是基于Multinomial Distribution的另一种方法。

在这里使用很方便,因为根据定义,所有块的总和(正在构建的块加上左右边距)自动等于视图大小。

所以首先我们对所有块进行采样,第二个在块内我们对边距进行采样并获得建筑物位置

代码,Python 3.8 Win 10 x64

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from PIL import Image, ImageDraw

rng = np.random.default_rng()

building_start = 25
building_end   = 975
nof_buildings  = 50

p = nof_buildings*[1.0/nof_buildings] # probabilities

q = None
while True:
    q = rng.multinomial(building_end-building_start, p)

    if np.any(q < 7): # minimum width condition, continue sampling
        continue

    break

print(q) # sampled blocks array
print(np.sum(q)) # be sure sum is equal to 950

# making positions/separators
min_height       = 100
max_height       = 400

buildings = list()
left = building_start
for k in range(0, nof_buildings):
    # position a block
    left +=  (0 if k == 0 else q[k])
    rght  = left + (0 if k == nof_buildings-1 else q[k]-1)

    # add margins
    left += rng.choice([1, 2, 3], p=[0.4, 0.35, 0.25])
    rght -= rng.choice([1, 2, 3], p=[0.4, 0.35, 0.25])

    height = rng.integers(min_height, max_height)

    buildings.append((left, rght, height)) # here is single building

im   = Image.new('RGB', (1024, 512), (255, 255, 255))
draw = ImageDraw.Draw(im)

for bld in buildings: # plot all buildings
    l    = bld[0]
    r    = bld[1]
    h    = max_height - bld[2]
    color = (rng.integers(128, 230),
             rng.integers(128, 230),
             rng.integers(128, 230))
    draw.rectangle((l, max_height, r, h), fill=color)

im.save('buildings.jpg')

最后你会得到类似

的东西