Skip to content

Instantly share code, notes, and snippets.

@shspage
Last active May 27, 2020 10:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shspage/36ad575668db9d7eed9f284ba18b0059 to your computer and use it in GitHub Desktop.
Save shspage/36ad575668db9d7eed9f284ba18b0059 to your computer and use it in GitHub Desktop.
以前書いたドロネー分割簡易最適化のblender版 (blender 2.82a)
import bpy
import mathutils
import numpy as np
import math
NUM_POINTS = 300 # 点の数
MAX_ITERATION = 100 # 最適化試行回数上限
MAX_ERROR_SQUARED = 1e-4 # 前回の最適化との最大誤差(の2乗)
RADIUS = 5 # 半径
def createDelaunay(points):
"""
Args:
points (list of (float,float))
Returns:
vertices (list of Vector(2d))
edges (list of (int,int))
faces (list of list of int)
"""
vs = [mathutils.Vector(x) for x in points]
result = mathutils.geometry.delaunay_2d_cdt(vs, [], [], 0, 0.001)
vertices, edges, faces = result[:3]
return vertices, edges, faces
def createDelaunayMesh(points, object_name):
"""
Args:
points (list of (float,float))
object_name (str)
"""
vertices, edges, faces = createDelaunay(points)
[v.resize_3d() for v in vertices]
msh = bpy.data.meshes.new(name=object_name + "_mesh")
msh.from_pydata(vertices, edges, faces)
msh.update()
obj = bpy.data.objects.new(name=object_name, object_data=msh)
bpy.context.scene.collection.objects.link(obj)
def main():
# 初期値の点を作成
points = np.random.randn(NUM_POINTS, 2) * RADIUS
if True:
points, edges, faces = createDelaunay(points)
points = np.array(points)
faces = np.array(faces)
for n in range(MAX_ITERATION):
if n % 5 == 0:
print(n, end=" ")
new_points = [] # 調整後の点のリスト
ng = False
# 各点(i=index)について以下を行う
for i in range(len(points)):
# 点i
point = points[i]
# 点iを含む三角形を抽出
idxs = faces[(faces == i).any(axis=1)]
# indexの重複を取り除き、各indexの出現回数を取得
idxs, counts = np.unique(idxs, return_counts=1)
# 点iは以下の計算から除外(...しなくてもよい?)
idxs = idxs[idxs != i]
# indexに対応する座標を取得
ps = points.take(idxs, axis=0)
# 座標の平均値を取得。調整後の点iの位置とする
avg = np.mean(ps, axis=0)
# 点iが外周上の場合、円周上まで移動させる
# (出現回数1の点があれば、点iは三角形で囲まれていない)
if (counts == 1).any():
avg = avg / np.sqrt(np.sum(avg**2)) * RADIUS
# 元の座標との距離(の2乗)が大きければ要再処理とする
if np.sum((avg - point)**2) > MAX_ERROR_SQUARED:
ng = True
new_points.append(avg)
points, edges, faces = createDelaunay(new_points)
points = np.array(points)
faces = np.array(faces)
if not ng:
print("break:%d / %d" % (n, MAX_ITERATION))
break
createDelaunayMesh(points, "delaunay")
if __name__=="__main__":
main()
import bpy
import math
# (続き)円形のメッシュを半球に変形
RADIUS = 5
for v in bpy.data.objects['delaunay'].data.vertices:
t = v.co.length / RADIUS * math.pi / 2
v1 = v.co.normalized() * math.sin(t)
v1.z = math.cos(t)
v.co = v1 * RADIUS
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment