二维平面生成无重叠随机不规则轮廓模拟砂岩微观结构技术咨询
实现思路
整合两个已有模块的核心是不要一开始就对任意贝塞尔形状做高成本碰撞检测,直接用「外接圆预排布+形状约束生成+细粒度校验补漏」的流程即可,能100%复用你已经写好的代码:
- 先运行已有的无重叠圆形生成逻辑,把每个圆作为后续不规则颗粒的最大外接边界,圆和圆之间留3%~8%的冗余间隙,大幅降低后续形状重叠概率
- 改造已有的闭合贝塞尔曲线生成代码,所有控制点全部约束在对应外接圆的半径范围内,控制点之间做平滑插值生成圆角,保证生成的颗粒轮廓不会超出外接圆边界
- 所有颗粒生成后做一次轻量碰撞校验:对中心距离小于两个半径和的邻近颗粒,采样轮廓点判断是否存在越界重叠,有重叠就微调颗粒位置或者重新生成该位置的贝塞尔轮廓,直到全部无重叠
可直接运行的单脚本
import numpy as np import matplotlib.pyplot as plt from scipy.special import binom import random # 闭合贝塞尔曲线计算 def bernstein(n, k, t): return binom(n, k) * t**k * (1 - t)**(n - k) def bezier(points, sample_num=200): n = len(points) - 1 t = np.linspace(0, 1, sample_num) curve = np.zeros((sample_num, 2)) for i in range(n + 1): curve += np.outer(bernstein(n, i, t), points[i]) return curve class SandGrain: def __init__(self, center, radius, irregularity=0.25, ctrl_point_num=7): self.center = np.array(center) self.radius = radius # 生成带随机扰动的控制点,全部约束在半径范围内 angles = np.linspace(0, 2*np.pi, ctrl_point_num, endpoint=False) # 随机扰动半径,生成不规则轮廓 point_radii = radius * (1 - irregularity * np.random.rand(ctrl_point_num)) ctrl_points = np.stack([ self.center[0] + point_radii * np.cos(angles), self.center[1] + point_radii * np.sin(angles) ], axis=1) # 闭合曲线 ctrl_points = np.vstack([ctrl_points, ctrl_points[0]]) # 生成平滑圆角轮廓 self.contour = bezier(ctrl_points, sample_num=280) def is_overlap(self, other, min_gap=1.5): # 远距离直接跳过检测,提速 center_dist = np.linalg.norm(self.center - other.center) if center_dist > self.radius + other.radius + min_gap: return False # 近距离采样点判断重叠 for p in self.contour[::8]: if np.linalg.norm(p - other.center) < other.radius * 0.92: return True return False def generate_sand_structure(canvas_size=500, grain_count=220, radius_range=(8, 32), max_retry=1200): grains = [] retry_count = 0 while len(grains) < grain_count and retry_count < max_retry: retry_count += 1 r = random.uniform(*radius_range) x = random.uniform(r, canvas_size - r) y = random.uniform(r, canvas_size - r) new_grain = SandGrain((x, y), r) # 碰撞校验 overlap_flag = False for g in grains: if new_grain.is_overlap(g): overlap_flag = True break if not overlap_flag: grains.append(new_grain) retry_count = 0 return grains # 可视化输出 if __name__ == "__main__": grain_list = generate_sand_structure() fig, ax = plt.subplots(figsize=(8, 8)) for grain in grain_list: ax.fill(grain.contour[:,0], grain.contour[:,1], c='#c7b788', ec='#806b4e', linewidth=0.6) ax.set_xlim(0, 500) ax.set_ylim(0, 500) ax.set_aspect('equal') plt.axis('off') plt.show()
可选优化方案
- 如果需要更贴近真实砂岩的孔隙分布,可以改用随机序列吸附+颗粒膨胀逻辑:先生成大量小尺寸颗粒占位,再逐步给每个颗粒加半径,碰到相邻颗粒就停止膨胀,生成的结构自然度比纯随机排布高很多
- 如果要生成高密度颗粒样本,可以把碰撞检测换成像素掩码判断,比逐点计算轮廓距离快3~5倍
- 可以调整
irregularity参数控制颗粒的不规则程度,数值越大颗粒形状越偏离圆形,越接近真实砂岩颗粒的棱角感
效果参考


内容的提问来源于stack exchange,提问作者Alpha
相关产品推荐
相关产品推荐

