Last active
September 28, 2025 08:09
-
-
Save pgtwitter/e697df75c73cc5e257b83cbf3c82de54 to your computer and use it in GitHub Desktop.
五角形の平面充填された影を作るオブジェクトを生成するbpyコード (タイルタイプ1,3,5,7,9,11,13,15のみ) (タイル参考情報 https://tilingpackingcovering.web.fc2.com/abstract.html
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import bpy | |
import bmesh | |
from mathutils import Vector | |
import math | |
# タイル参考情報 https://tilingpackingcovering.web.fc2.com/abstract.html | |
# code by Grok ここから | |
def reflect_points(points, idx1, idx2): | |
""" | |
点のリストを、指定した2点(インデックスで指定)を結ぶ直線で線対称変換する。 | |
points: 座標リスト [(x1, y1), (x2, y2), ...] | |
idx1, idx2: 対称軸を定義する2点のインデックス | |
戻り値: 変換後の座標リスト | |
""" | |
# 対称軸の2点 | |
x1, y1 = points[idx1] | |
x2, y2 = points[idx2] | |
# 対称軸が同一点の場合、エラー回避のため元の点を返す | |
if abs(x1 - x2) < 1e-6 and abs(y1 - y2) < 1e-6: | |
return points[:] | |
new_points = [] | |
for x, y in points: | |
# 対称軸の直線の方程式 ax + by + c = 0 を求める | |
# 直線は点 (x1, y1), (x2, y2) を通る | |
if abs(x2 - x1) < 1e-6: # 垂直な直線(x = x1) | |
# 鏡像点: (x, y) -> (2*x1 - x, y) | |
x_new = 2 * x1 - x | |
y_new = y | |
else: | |
# 直線の傾き m と切片 c | |
m = (y2 - y1) / (x2 - x1) | |
c = y1 - m * x1 | |
# 点 (x, y) から直線への垂線の交点 (xp, yp) | |
# 直線 ax + by + c = 0 の場合、a = m, b = -1, c = c | |
# 交点計算: https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line | |
denom = m**2 + 1 | |
xp = (x + m * y - m * c) / denom | |
yp = (m * (x + m * y) + c) / denom | |
# 鏡像点: (x, y) -> (2*xp - x, 2*yp - y) | |
x_new = 2 * xp - x | |
y_new = 2 * yp - y | |
new_points.append((x_new, y_new)) | |
return new_points | |
def rotate_points(points, theta_degrees, center=(0, 0)): | |
""" | |
点のリストを指定した角度(度)で回転させる。 | |
points: 座標リスト [(x1, y1), (x2, y2), ...] | |
theta_degrees: 回転角度(度) | |
center: 回転中心(デフォルトは原点 (0, 0)) | |
""" | |
theta = math.radians(theta_degrees) | |
cos_theta = math.cos(theta) | |
sin_theta = math.sin(theta) | |
new_points = [] | |
for x, y in points: | |
x -= center[0] | |
y -= center[1] | |
x_new = x * cos_theta - y * sin_theta | |
y_new = x * sin_theta + y * cos_theta | |
x_new += center[0] | |
y_new += center[1] | |
new_points.append((x_new, y_new)) | |
return new_points | |
def translate_points(points, tx, ty): | |
""" | |
点のリストを指定した量だけ平行移動させる。 | |
points: 座標リスト [(x1, y1), (x2, y2), ...] | |
tx, ty: X方向、Y方向の移動量 | |
""" | |
return [(x + tx, y + ty) for x, y in points] | |
def merge_graphs(graphs, threshold=1e-6): | |
""" | |
複数のグラフを一括で統合し、重なった頂点と辺をまとめた新しいグラフを返す。 | |
graphs: グラフのリスト [(points1, edges1), (points2, edges2), ...] | |
threshold: 頂点が重なる判定の閾値 | |
戻り値: (統合後のポイントリスト, 統合後の辺リスト) | |
""" | |
# 全ての頂点を収集(グラフID、頂点インデックス、座標) | |
vertices = [] | |
for graph_idx, (points, _) in enumerate(graphs): | |
for point_idx, pos in enumerate(points): | |
vertices.append((graph_idx, point_idx, pos)) | |
# 頂点を座標でグループ化(重なり判定) | |
vertex_groups = [] | |
used = set() | |
for i, (graph_idx1, point_idx1, pos1) in enumerate(vertices): | |
if i in used: | |
continue | |
group = [(graph_idx1, point_idx1, pos1)] | |
for j, (graph_idx2, point_idx2, pos2) in enumerate(vertices[i+1:], i+1): | |
if j not in used: | |
dx = pos1[0] - pos2[0] | |
dy = pos1[1] - pos2[1] | |
if math.sqrt(dx*dx + dy*dy) < threshold: | |
group.append((graph_idx2, point_idx2, pos2)) | |
used.add(j) | |
vertex_groups.append(group) | |
used.add(i) | |
# 新しい頂点リストを作成 | |
new_points = [group[0][2] for group in vertex_groups] # 代表座標 | |
# 辺を統合 | |
node_map = {} # (graph_idx, point_idx) -> new_idx | |
for new_idx, group in enumerate(vertex_groups): | |
for graph_idx, point_idx, _ in group: | |
node_map[(graph_idx, point_idx)] = new_idx | |
new_edges = set() | |
for graph_idx, (_, edges) in enumerate(graphs): | |
for u, v in edges: | |
new_u = node_map.get((graph_idx, u)) | |
new_v = node_map.get((graph_idx, v)) | |
if new_u is not None and new_v is not None: | |
new_edges.add(tuple(sorted((new_u, new_v)))) | |
return new_points, list(new_edges) | |
def project_to_sphere(x_Q, y_Q, R=1.0): | |
denom = x_Q * x_Q + y_Q * y_Q + 4 * R * R | |
x_P = 4 * R * R * x_Q / denom | |
y_P = 4 * R * R * y_Q / denom | |
z_P = R * (x_Q * x_Q + y_Q * y_Q - 4 * R * R) / denom | |
return Vector((x_P, y_P, z_P)), z_P | |
# code by Grok ここまで | |
def length(p0, p1): | |
return math.sqrt((p0[0]-p1[0])**2+(p0[1]-p1[1])**2) | |
def centering(points): | |
# 中心に移動 | |
max_x, max_y = map(max, zip(*points)) | |
min_x, min_y = map(min, zip(*points)) | |
tx = -(max_x + min_x) / 2.0 | |
ty = -(max_y + min_y) / 2.0 | |
result_points = translate_points(points, tx, ty) | |
return result_points | |
def sub_p1_from_p0(p0, p1): | |
return [p1[0]-p0[0], p1[1]-p0[1]] | |
def square(): | |
points = [ | |
(0, 0), # A | |
(1, 0), # B | |
(1, 1), # C | |
(0, 1), # D | |
] | |
edges = [(0, 1), (1, 2), (2, 3), (3, 0)] | |
points_new = [p for p in points] | |
edges_new = [e for e in edges] | |
v0 = sub_p1_from_p0(points_new[3], points_new[2]) | |
v1 = sub_p1_from_p0(points_new[3], points_new[0]) | |
scale = 0.25 | |
points_new = [(x*scale, y*scale) for x, y in points_new] | |
v = [(x*scale, y*scale) for x, y in [v0, v1]] | |
return points_new, edges_new, v[0], v[1], 22, 22, 0 | |
def hexagon(): | |
c30 = math.cos(math.radians(30)) | |
s30 = math.sin(math.radians(30)) | |
points = [ | |
(0, 0), # A | |
(c30, s30), # B | |
(c30, s30+1), # C | |
(0, 2*s30+1), # D | |
(-c30, s30+1), # E | |
(-c30, s30), # F | |
] | |
edges = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 0)] | |
points_new = [p for p in points] | |
edges_new = [e for e in edges] | |
v0 = sub_p1_from_p0(points_new[4], points_new[2]) | |
v1 = sub_p1_from_p0(points_new[4], points_new[0]) | |
scale = 0.4 | |
points_new = [(x*scale, y*scale) for x, y in points_new] | |
v = [(x*scale, y*scale) for x, y in [v0, v1]] | |
return points_new, edges_new, v[0], v[1], 22, 18, 0 | |
def circle(): | |
n = 36 | |
delta = 2 * math.pi / n | |
c30 = math.cos(math.radians(30)) | |
s30 = math.sin(math.radians(30)) | |
points = [(math.cos(delta*i), math.sin(delta*i)) for i in range(n)] | |
edges = [(i, i+1) for i in range(n-1)] | |
edges.append((n-1, 0)) | |
points_new = [p for p in points] | |
edges_new = [e for e in edges] | |
v0, v1 = (2*c30, 2*s30), (-2*c30, 2*s30) | |
scale = 0.35 | |
points_new = [(x*scale, y*scale) for x, y in points_new] | |
v = [(x*scale, y*scale) for x, y in [v0, v1]] | |
return points_new, edges_new, v[0], v[1], 18, 22, math.pi/6 | |
def type1(): | |
rt3 = math.sqrt(3) | |
alpha = 4/3 * rt3 | |
# タイルを作成 | |
points = [ | |
(0, 0), # A | |
(1, rt3), # B | |
(0, alpha), # C | |
(-2, alpha), # D | |
(-1, 0) # E | |
] | |
edges = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)] | |
# 回転と平行移動を適用 | |
points_rotated = rotate_points(points, 180, center=(0, 0)) | |
points_transformed = translate_points(points_rotated, 1, rt3) | |
# グラフを統合 | |
points_new, edges_new = merge_graphs([(points, edges), (points_transformed, edges)]) | |
v0 = sub_p1_from_p0(points_new[3], points_new[7]) | |
v1 = sub_p1_from_p0(points_new[1], points_new[5]) | |
scale = 0.2 | |
points_new = [(x*scale, y*scale) for x, y in points_new] | |
v = [(x*scale, y*scale) for x, y in [v0, v1]] | |
return points_new, edges_new, v[0], v[1], 8, 26, 0 | |
def type3(): | |
c30 = math.cos(math.radians(30)) | |
s30 = math.sin(math.radians(30)) | |
cAlpha = math.cos(math.radians(79.5-30)) | |
sAlpha = math.sin(math.radians(79.5-30)) | |
cBeta = math.cos(math.radians(79.5-30+120)) | |
sBeta = math.sin(math.radians(79.5-30+120)) | |
# タイルを作成 | |
points = [ | |
(0, 0), # A | |
(c30, s30), # B | |
(c30-0.75*cAlpha, s30+0.75*sAlpha), # C | |
(c30-0.75*cAlpha+1.14*cBeta, s30+0.75*sAlpha-1.14*sBeta), # D | |
(-c30, s30) # E | |
] | |
edges = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)] | |
points = [(x, -y) for x, y in points] | |
points_reflect = rotate_points(points, 120, (0, 0)) | |
points_new, edges_new = merge_graphs([(points, edges), (points_reflect, edges)]) | |
points_reflect2 = rotate_points(points, 240, (0, 0)) | |
points_new, edges_new = merge_graphs([(points_new, edges_new), (points_reflect2, edges)]) | |
v0 = sub_p1_from_p0(points_reflect[2], points[2]) | |
v1 = sub_p1_from_p0(points_reflect2[2], points[2]) | |
scale = 0.4 | |
points_new = [(x*scale, y*scale) for x, y in points_new] | |
v = [(x*scale, y*scale) for x, y in [v0, v1]] | |
return points_new, edges_new, v[0], v[1], 20, 20, math.pi/6 | |
def type5(): | |
c30 = math.cos(math.radians(30)) | |
s30 = math.sin(math.radians(30)) | |
c75 = math.cos(math.radians(75)) | |
s75 = math.sin(math.radians(75)) | |
c15 = math.cos(math.radians(15)) | |
s15 = math.sin(math.radians(15)) | |
points = [ | |
(0, 0), # A | |
(c30, s30), # B | |
(c30+2*c75, s30+2*s75), # C | |
(c30+2*c75-2*c15, s30+2*s75-2*s15), # D | |
(-c30, s30) # E | |
] | |
edges = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)] | |
points_new = [p for p in points] | |
edges_new = [e for e in edges] | |
for i in range(5): | |
points_rotate = rotate_points(points, 60*(i+1), points[2]) | |
points_new, edges_new = merge_graphs([(points_new, edges_new), (points_rotate, edges_new)]) | |
v0 = sub_p1_from_p0(points_new[18], points_new[6]) | |
v1 = sub_p1_from_p0(points_new[16], points_new[1]) | |
scale = 0.2 | |
points_new = [(x*scale, y*scale) for x, y in points_new] | |
v = [(x*scale, y*scale) for x, y in [v0, v1]] | |
return points_new, edges_new, v[0], v[1], 16, 16, math.pi/6 | |
def type7(): | |
c45 = math.cos(math.radians(45)) | |
s45 = math.sin(math.radians(45)) | |
c135 = math.cos(math.radians(135)) | |
s135 = math.sin(math.radians(135)) | |
points = [ | |
(0, 0), # A | |
(1, 0), # B | |
(1+c45, s45), # C | |
(1+c45-c45, s45+s45), # D | |
(c135, s135) # E | |
] | |
edges = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)] | |
points_new = [p for p in points] | |
edges_new = [e for e in edges] | |
points_reflect = reflect_points(points, 3, 4) | |
points_new, edges_new = merge_graphs([(points_new, edges_new), (points_reflect, edges)]) | |
points_rotate = rotate_points(points_new, 180+45, points_new[2]) | |
t = sub_p1_from_p0(points_rotate[0], points_new[2]) | |
points_transformed = translate_points(points_rotate, t[0], t[1]) | |
points_new2, edges_new2 = merge_graphs([(points_new, edges_new), (points_transformed, edges_new)]) | |
points_rotate1 = rotate_points(points_new, 45, points_new[2]) | |
t = sub_p1_from_p0(points_rotate1[2], points_new[1]) | |
points_transformed1 = translate_points(points_rotate1, t[0], t[1]) | |
points_new3, edges_new3 = merge_graphs([(points_new2, edges_new2), (points_transformed1, edges_new)]) | |
points_rotate2 = rotate_points(points_new, 180, points_new[2]) | |
t = sub_p1_from_p0(points_rotate2[2], points_new3[14]) | |
points_transformed2 = translate_points(points_rotate2, t[0], t[1]) | |
points_new, edges_new = merge_graphs([(points_new3, edges_new3), (points_transformed2, edges_new)], 0.1) | |
v0 = sub_p1_from_p0(points_new[3], points_new[16]) | |
v1 = sub_p1_from_p0(points_new[6], points_new[12]) | |
scale = 0.2 | |
points_new = [(x*scale, y*scale) for x, y in points_new] | |
v = [(x*scale, y*scale) for x, y in [v0, v1]] | |
return points_new, edges_new, v[0], v[1], 16, 12, math.pi/4 | |
def type9(): | |
a = 127.912 | |
b = 72 | |
alpha = 180-a | |
beta = b-alpha | |
gamma = 540-((360-a)+(360-b))/2-a-b | |
cAlpha = math.cos(math.radians(alpha)) | |
sAlpha = math.sin(math.radians(alpha)) | |
cBeta = math.cos(math.radians(beta)) | |
sBeta = math.sin(math.radians(beta)) | |
cGamma = math.cos(math.radians(gamma)) | |
sGamma = math.sin(math.radians(gamma)) | |
points = [ | |
(0, 0), # A | |
(1, 0), # B | |
(1+cAlpha, sAlpha), # C | |
(1+cAlpha-cBeta, sAlpha+sBeta), # D | |
(cGamma, sGamma) # E | |
] | |
edges = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)] | |
points_new = [p for p in points] | |
edges_new = [e for e in edges] | |
points1 = reflect_points(points, 3, 4) | |
points_new, edges_new = merge_graphs([(points_new, edges_new), (points1, edges)]) | |
t = sub_p1_from_p0(points[1], points_new[4]) | |
points1 = translate_points(points, t[0], t[1]) | |
points_rotate1 = rotate_points(points1, gamma, points1[1]) | |
points_new, edges_new = merge_graphs([(points_new, edges_new), (points_rotate1, edges)]) | |
points2 = reflect_points(points_rotate1, 3, 4) | |
points_new, edges_new = merge_graphs([(points_new, edges_new), (points2, edges)]) | |
points_rotate2 = rotate_points(points_new, 180, points_new[12]) | |
t = sub_p1_from_p0(points_new[12], points_new[8]) | |
points_transformed = translate_points(points_rotate2, t[0], t[1]) | |
points_new, edges_new = merge_graphs([(points_new, edges_new), (points_transformed, edges_new)]) | |
v0 = sub_p1_from_p0(points_new[3], points_new[15]) | |
v1 = sub_p1_from_p0(points_new[6], points_new[9]) | |
scale = 0.4 | |
points_new = [(x*scale, y*scale) for x, y in points_new] | |
v = [(x*scale, y*scale) for x, y in [v0, v1]] | |
return points_new, edges_new, v[0], v[1], 6, 16, beta/2 | |
def type11(): | |
alpha = 180-150 | |
cAlpha = math.cos(math.radians(alpha)) | |
sAlpha = math.sin(math.radians(alpha)) | |
beta = 60-alpha | |
cBeta = math.cos(math.radians(beta)) | |
sBeta = math.sin(math.radians(beta)) | |
b = 3*math.sqrt(3) | |
c = 2 | |
d = 4 | |
points = [ | |
(0, 0), # A | |
(b, 0), # B | |
(b+c*cAlpha, c*sAlpha), # C | |
(b+c*cAlpha-d*cBeta, c*sAlpha+d*sBeta), # D | |
(0, 1) # E | |
] | |
edges = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)] | |
points_new = [p for p in points] | |
edges_new = [e for e in edges] | |
points1 = reflect_points(points, 0, 1) | |
points_new, edges_new = merge_graphs([(points_new, edges_new), (points1, edges)]) | |
points_rotate = rotate_points(points_new, 60, points_new[3]) | |
t = sub_p1_from_p0(points_rotate[4], points_new[3]) | |
points_transformed = translate_points(points_rotate, t[0], t[1]) | |
points_rotate1 = rotate_points(points_new, 180, points_new[1]) | |
t = sub_p1_from_p0(points_rotate1[1], points_new[2]) | |
points_transformed1 = translate_points(points_rotate1, t[0], t[1]) | |
points_rotate2 = rotate_points(points_transformed1, 60, points_transformed1[3]) | |
t = sub_p1_from_p0(points_rotate2[7], points_new[5]) | |
points_transformed2 = translate_points(points_rotate2, t[0], t[1]) | |
points_new, edges_new = merge_graphs([(points_new, edges_new), (points_transformed, edges_new)]) | |
points_new, edges_new = merge_graphs([(points_new, edges_new), (points_transformed1, edges_new)]) | |
points_new, edges_new = merge_graphs([(points_new, edges_new), (points_transformed2, edges_new)]) | |
v0 = sub_p1_from_p0(points_new[11], points_new[16]) | |
v1 = sub_p1_from_p0(points_new[18], points_new[6]) | |
scale = 0.2 | |
points_new = [(x*scale, y*scale) for x, y in points_new] | |
v = [(x*scale, y*scale) for x, y in [v0, v1]] | |
return points_new, edges_new, v[0], v[1], 10, 10, math.pi/3 | |
def type13(): | |
alpha = 180-110 | |
cAlpha = math.cos(math.radians(alpha)) | |
sAlpha = math.sin(math.radians(alpha)) | |
cBeta = math.cos(math.radians(90-alpha)) | |
sBeta = math.sin(math.radians(90-alpha)) | |
b = 4.15 | |
c = 1.68 | |
points = [ | |
(0, 0), # A | |
(b, 0), # B | |
(b+c*cAlpha, c*sAlpha), # C | |
(b+c*cAlpha-c*cBeta, c*sAlpha+c*sBeta), # D | |
(0, 1) # E | |
] | |
edges = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)] | |
points_reflect = reflect_points(points, 0, 1) | |
points, edges = merge_graphs([(points, edges), (points_reflect, edges)]) | |
points_new = [p for p in points] | |
edges_new = [e for e in edges] | |
points_reflect2 = reflect_points(points_new, 4, 0) | |
points_new, edges_new = merge_graphs([(points_new, edges_new), (points_reflect2, edges_new)]) | |
points_rotate = rotate_points(points, -90, (0, 0)) | |
t = sub_p1_from_p0(points_rotate[1], points_new[3]) | |
points_transformed = translate_points(points_rotate, t[0], t[1]) | |
points_new, edges_new = merge_graphs([(points_new, edges_new), (points_transformed, edges_new)]) | |
points_rotate2 = rotate_points(points, 90, (0, 0)) | |
t = sub_p1_from_p0(points_rotate2[1], points_reflect2[6]) | |
points_transformed2 = translate_points(points_rotate2, t[0], t[1]) | |
points_new, edges_new = merge_graphs([(points_new, edges_new), (points_transformed2, edges_new)]) | |
v0 = sub_p1_from_p0(points_new[10], points_new[7]) | |
v1 = sub_p1_from_p0(points_new[15], points_new[20]) | |
scale = .1 | |
points_new = [(x*scale, y*scale) for x, y in points_new] | |
v = [(x*scale, y*scale) for x, y in [v0, v1]] | |
return points_new, edges_new, v[0], v[1], 24, 8, math.pi/4 | |
def type15(): | |
rt2 = math.sqrt(2) | |
rt3 = math.sqrt(3) | |
c15 = math.cos(math.pi/12) | |
s15 = math.sin(math.pi/12) | |
c30 = math.cos(math.pi/6) | |
s30 = math.sin(math.pi/6) | |
points = [ | |
(0, 0), # A | |
(1, 0), # B | |
(1+2*c30, 2*s30), # C | |
(1+c30, 3*s30), # D | |
(0, 1) # E | |
] | |
edges = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)] | |
points_reflect = reflect_points(points, 1, 2) | |
points_reflect2 = reflect_points(points_reflect, 3, 4) | |
points1, edges1 = merge_graphs([(points, edges), (points_reflect, edges)]) | |
points2, edges2 = merge_graphs([(points1, edges1), (points_reflect2, edges)]) | |
points_rotated = rotate_points(points2, 180, center=(0, 0)) | |
points_transformed = translate_points(points_rotated, (1+c30)+(1+2*c30), 2+s30) | |
points_new, edges_new = merge_graphs([(points2, edges2), (points_transformed, edges2)]) | |
points_reflect3 = reflect_points(points2, 0, 4) | |
points_transformed1 = translate_points(points_reflect3, (1+rt2*s15)+(1+c30), (rt2*c15)+(3*s30+1+2*c30)) | |
points_new, edges_new = merge_graphs([(points_new, edges_new), (points_transformed1, edges2)]) | |
l = length(points[3], points[4]) | |
points_reflect4 = reflect_points(points2, 0, 1) | |
points_transformed2 = translate_points(points_reflect4, (-l/rt2)+(1+2*c30), (-l/rt2)+(-2*c30)) | |
points_new, edges_new = merge_graphs([(points_new, edges_new), (points_transformed2, edges2)]) | |
v0 = sub_p1_from_p0(points_new[28], points_new[21]) | |
v1 = sub_p1_from_p0(points_new[23], points_new[33]) | |
scale = .2 | |
points_new = [(x*scale, y*scale) for x, y in points_new] | |
v = [(x*scale, y*scale) for x, y in [v0, v1]] | |
return points_new, edges_new, v[0], v[1], 20, 8, 0 | |
def translationalUnits(n): | |
if n == 1: | |
return type1() | |
elif n == 3: | |
return type3() | |
elif n == 5: | |
return type5() | |
elif n == 7: | |
return type7() | |
elif n == 9: | |
return type9() | |
elif n == 11: | |
return type11() | |
elif n == 13: | |
return type13() | |
elif n == 15: | |
return type15() | |
elif n == 'square': | |
return square() | |
elif n == 'hexagon': | |
return hexagon() | |
elif n == 'circle': | |
return circle() | |
raise Exception("未実装") | |
def create_curve(R, t=1): | |
points_new, edges_new, v0, v1, ny, nx, rz = translationalUnits(t) | |
graphs = [] | |
for i in range(ny): | |
for j in range(nx): | |
x = i * v0[0] + j * v1[0] | |
y = i * v0[1] + j * v1[1] | |
graphs.append((translate_points(points_new, x, y), edges_new)) | |
tmp_points, tmp_edges = merge_graphs(graphs) | |
tmp_points = centering(tmp_points) | |
vertices = [] | |
edges = [] | |
for po in centering(tmp_points): | |
P, z_P = project_to_sphere(po[0], po[1], R) | |
vertices.append(P) | |
for e in tmp_edges: | |
edges.append((e[0], e[1])) | |
mesh = bpy.data.meshes.new("Curves") | |
obj = bpy.data.objects.new("Curves", mesh) | |
obj.rotation_euler = (0, 0, rz) | |
bpy.context.collection.objects.link(obj) | |
bpy.context.view_layer.objects.active = obj | |
bm = bmesh.new() | |
bm_verts = [bm.verts.new(v) for v in vertices] | |
for i, j in edges: | |
bm.edges.new((bm_verts[i], bm_verts[j])) | |
bm.to_mesh(mesh) | |
bm.free() | |
bpy.ops.object.convert(target='CURVE') | |
return obj | |
def curve2mesh(obj, R): | |
bpy.ops.object.select_all(action='SELECT') | |
bpy.ops.object.convert(target='CURVE') | |
bpy.context.view_layer.objects.active = obj | |
bpy.ops.object.modifier_add(type='NODES') | |
mod = obj.modifiers.active | |
bpy.ops.node.new_geometry_node_group_assign() | |
nodes = mod.node_group.nodes | |
links = mod.node_group.links | |
gid = nodes['Group Input'] | |
gout = nodes['Group Output'] | |
ctm = nodes.new(type='GeometryNodeCurveToMesh') | |
if True: | |
resample = nodes.new(type='GeometryNodeResampleCurve') | |
resample.mode = 'LENGTH' | |
resample.inputs['Length'].default_value = 0.005 | |
links.new(gid.outputs['Geometry'], resample.inputs['Curve']) | |
setPos = nodes.new(type='GeometryNodeSetPosition') | |
links.new(resample.outputs['Curve'], setPos.inputs['Geometry']) | |
pos = nodes.new(type='GeometryNodeInputPosition') | |
subtract = nodes.new(type='ShaderNodeVectorMath') | |
subtract.operation = 'SUBTRACT' | |
subtract.inputs[1].default_value = (0, 0, R) | |
links.new(pos.outputs['Position'], subtract.inputs[0]) | |
normalize = nodes.new(type='ShaderNodeVectorMath') | |
normalize.operation = 'NORMALIZE' | |
links.new(subtract.outputs['Vector'], normalize.inputs['Vector']) | |
sphere = nodes.new(type='GeometryNodeMeshUVSphere') | |
raycast = nodes.new(type='GeometryNodeRaycast') | |
raycast.inputs['Ray Length'].default_value = 100 | |
links.new(sphere.outputs['Mesh'], raycast.inputs['Target Geometry']) | |
links.new(pos.outputs['Position'], raycast.inputs['Source Position']) | |
links.new(normalize.outputs['Vector'], raycast.inputs['Ray Direction']) | |
links.new(raycast.outputs['Is Hit'], setPos.inputs['Selection']) | |
links.new(raycast.outputs['Hit Position'], setPos.inputs['Position']) | |
links.new(setPos.outputs['Geometry'], ctm.inputs['Curve']) | |
else: | |
links.new(gid.outputs['Geometry'], ctm.inputs['Curve']) | |
pos2 = nodes.new(type='GeometryNodeInputPosition') | |
sep = nodes.new(type='ShaderNodeSeparateXYZ') | |
links.new(pos2.outputs['Position'], sep.inputs['Vector']) | |
range = nodes.new(type='ShaderNodeMapRange') | |
range.inputs['From Min'].default_value = -1 | |
range.inputs['From Max'].default_value = 1 | |
range.inputs['To Min'].default_value = 0.01 | |
range.inputs['To Max'].default_value = 0.0001 | |
links.new(sep.outputs['Z'], range.inputs['Value']) | |
links.new(ctm.outputs['Mesh'], gout.inputs['Geometry']) | |
links.new(range.outputs['Result'], ctm.inputs['Scale']) | |
cc = nodes.new(type='GeometryNodeCurvePrimitiveCircle') | |
cc.inputs['Resolution'].default_value = 8 | |
cc.inputs['Radius'].default_value = 1 | |
links.new(cc.outputs['Curve'], ctm.inputs['Profile Curve']) | |
return obj | |
def add_light(R, curves): | |
bpy.ops.object.light_add(type='SPOT', location=(0, 0, R)) | |
light = bpy.context.object | |
light.data.energy = 100 | |
light.data.spot_size = math.pi | |
light.data.spot_blend = 0.0 | |
light.parent = curves | |
def add_plane(R): | |
bpy.ops.mesh.primitive_plane_add(size=10, location=(0, 0, -R)) | |
def mod_world_shader(): | |
world = bpy.context.scene.world | |
nodes = world.node_tree.nodes | |
links = world.node_tree.links | |
gout = nodes['World Output'] | |
vs = nodes.new(type='ShaderNodeVolumeScatter') | |
vs.inputs['Density'].default_value = 0.1 | |
links.new(vs.outputs['Volume'], gout.inputs['Volume']) | |
def add_camera(): | |
scene = bpy.context.scene | |
bpy.ops.object.camera_add(location=(0, -8, 0.22), rotation=(math.radians(88), 0, 0)) | |
scene.camera = bpy.context.object | |
scene.camera.data.lens = 66 | |
scene.render.resolution_x = 1080 | |
scene.render.resolution_y = 1920 | |
def add_camera_top(R): | |
scene = bpy.context.scene | |
bpy.ops.object.camera_add(location=(0, 0, 2*R), rotation=(0, 0, 0)) | |
scene.camera = bpy.context.object | |
scene.camera.data.lens = 15 | |
scene.render.resolution_x = 1080 | |
scene.render.resolution_y = 1920 | |
bpy.ops.object.select_all(action='SELECT') | |
bpy.ops.object.delete() | |
R = 1 | |
tileType = 9 # 'square' | |
obj = curve2mesh(create_curve(R, tileType), R) | |
add_light(R, obj) | |
add_plane(R) | |
mod_world_shader() | |
add_camera_top(R) | |
add_camera() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment