Diamond


ちょっとくだらない理由で元町のジュエリー店に行ってきました。
ふと見るとカラット級の大粒ダイヤがあります。
見つめていると魂を吸い込まれていきそうになります。
値段を見ます。
ふむ、買えなくもない。
未だボーナスは手付かずだったな……。
これ、いただきます……。
はっ! 私は何を考えているのだ?
ダイヤに誘惑されている自分を必死に抑えます。

これほどまでにダイヤモンドは私の心を魅了します。
ラウンド・ブリリアントカット の大粒のダイヤ、ずっとずっと眺めていたい。
車のディーラーもそうなのですが、ジュエリー店には行ってはいけませんね。
欲しくなってしまうのです。
気が付いたら買う前提で店員と話をしていたりします。
危険極まりありません。

我に返ってジュエリー店を後にしたのですがダイヤモンドが脳裏から離れません。
欲しい、欲しい、手元に置いて眺めていたい。
しかし買うわけにはいかないのです。
子供の塾代もあるし、家のローンだってあるのです。
ということで3Dモデルで我慢することにしました。
どうせならば作りましょう。

ラウンドブリリアントカットは寸法比や角度が精密に定義されているものだとばかり思っていました。
調べてみると、比較的ゆるいルールしかないんですね。
まあ想像するに相手は自然から発掘される石ころなのだから、取れた原石に合わせてカットする自由度を残す必要があるのでしょうね。
とは言え、幾何学図形なのでだいたいの形はスクリプト生成できます。
以下ラウンドブリリアントカットの3Dオブジェクトデータを生成するpythonスクリプトです。

# -*- coding: utf-8 -*-
from mpl_toolkits.mplot3d import axes3d
import math
import matplotlib.pyplot as plt
import numpy as np

def rot_z(r, P):
    # z軸で回転行列を計算する
    Rz = np.array([[ np.cos(r),  np.sin(r),    0.0       ],
                   [-np.sin(r),  np.cos(r),    0.0       ],
                   [ 0.0,        0.0,          1.0       ]])
    R = Rz.dot(P.T)
    return R.T

def rot_y(r, P):
    # y軸で回転行列を計算する

    Ry = np.array([[ np.cos(py),  0.0,        -np.sin(py)],
                   [ 0.0,         1.0,         0.0       ],
                   [ np.sin(py),  0.0,         np.cos(py)]])
    R = Rz.dot(P.T)
    return R.T

def rot_x(r, P):
    # x軸で回転行列を計算する
    Rx = np.array([[ 1.0,         0.0,         0.0       ],
                   [ 0.0,         np.cos(px), -np.sin(px)],
                   [ 0.0,         np.sin(px),  np.cos(px)]])
    R = Rz.dot(P.T)
    return R.T

def mid_point(P , Q, par):
    pp = par / 100.0
    pq = (100.0 - par) / 100.0
    PP = np.array([pp, pp, pp])
    PQ = np.array([pq, pq, pq])
    P = P * PP
    Q = Q * PQ
    R = P + Q
    return R

def push_index(R):
    global x
    global y
    global z
    x.append(R[0])
    y.append(R[1])
    z.append(R[2])

def print_v(s, P):
    global v_num
    v_num += 1
    sr = s + ' '
    sr += ' {:.6f}'.format(P[0])
    sr += ' {:.6f}'.format(P[1])
    sr += ' {:.6f}'.format(P[2])
    sr += '\n'
    return(sr)

#定数
table_r = 0.55 / 2.0
girdle_r = 1.0 / 2.0
crown_height = 0.162
pavilion_height = 0.431
girdle_height = 0.025
star_ratio = 60.0
pavilion_ratio = 40.0
culet_ratio = 2.5
center = 50.0
t = math.tan(math.pi/8.0)

x = []
y = []
z = []
v_num = 0

# table(auxiliary point)
P_TA = np.array([])
P_TA = np.empty((0,3), float)
S = np.array([table_r, t * table_r, crown_height])
for num in range(0, 8):
    R = rot_z(math.pi / 4 * num, S)
    P_TA = np.append(P_TA, np.array([R]), axis=0)

# table
P_TB = np.array([])
P_TB = np.empty((0,3), float)
S = mid_point(P_TA[0] , P_TA[1], 50.0)
for num in range(0, 8):
    R = rot_z(math.pi / 4 * num, S)
    push_index(R)
    P_TB = np.append(P_TB, np.array([R]), axis=0)

# upper girdle(auxiliary point)
P_UGA = np.array([])
P_UGA = np.empty((0,3), float)
S = np.array([girdle_r, t * girdle_r, 0])
for num in range(0, 8):
    R = rot_z(math.pi / 4 * num, S)
#    push_index(R)
    P_UGA = np.append(P_UGA, np.array([R]), axis=0)

# upper girdle
P_UG = np.array([])
P_UG = np.empty((0,3), float)
S = mid_point(P_UGA[0] , P_UGA[1], center)
for num in range(0, 16):
    R = rot_z(math.pi / 8 * num, S)
    push_index(R)
    P_UG = np.append(P_UG, np.array([R]), axis=0)

# star facet
P_SF = np.array([])
P_SF = np.empty((0,3), float)
for num in range(0, 8):
    R = mid_point(P_TA[num] , P_UGA[num], star_ratio)
    push_index(R)
    P_SF = np.append(P_SF, np.array([R]), axis=0)

# culet
P_CU = np.array([0, 0, -pavilion_height - girdle_height])

# lower girdle(auxiliary point)
P_LGA = np.array([])
P_LGA = np.empty((0,3), float)
S = np.array([girdle_r, t * girdle_r, -girdle_height])
for num in range(0, 8):
    R = rot_z(math.pi / 4 * num, S)
#    push_index(R)
    P_LGA = np.append(P_LGA, np.array([R]), axis=0)

# lower girdle
P_LG = np.array([])
P_LG = np.empty((0,3), float)
S = mid_point(P_LGA[0] , P_LGA[1], center)
for num in range(0, 16):
    R = rot_z(math.pi / 8 * num, S)
    push_index(R)
    P_LG = np.append(P_LG, np.array([R]), axis=0)

# pavilion facet
P_PF = np.array([])
P_PF = np.empty((0,3), float)
for num in range(0, 8):
    R = mid_point(P_LGA[num] , P_CU, pavilion_ratio)
    push_index(R)
    P_PF = np.append(P_PF, np.array([R]), axis=0)

# culet table
P_QT = np.array([])
P_QT = np.empty((0,3), float)
for num in range(0, 8):
    R = mid_point(P_LGA[num] , P_CU, culet_ratio)
    push_index(R)
    P_QT = np.append(P_QT, np.array([R]), axis=0)

#make obj file
path_w = './test_diamond.obj'
s = 'New file'
with open(path_w, mode='w') as f:
    f.write('g diamond\n')
    for num in range(0, 8):
        f.write(print_v('v', P_TB[num]))
    for num in range(0, 8):
        f.write(print_v('v', P_SF[num]))
    for num in range(0, 16):
        f.write(print_v('v', P_UG[num]))
    for num in range(0, 16):
        f.write(print_v('v', P_LG[num]))
    for num in range(0, 8):
        f.write(print_v('v', P_PF[num]))
    for num in range(0, 8):
        f.write(print_v('v', P_QT[num]))

    t_base = 0
    s_base = 8
    ug_base = 16
    lg_base = 32
    p_base = 48
    c_base = 56

    #Table Facet: 1
    f.write('#Table Facet: num = 1\n')
    f.write('f 1 2 3 4 5 6 7 8\n')

    #Bezel Facet: 8
    f.write('#Bezel Facet: num = 8\n')
    f.write('f {:d} {:d} {:d} {:d}\n'.format(t_base + 1 , s_base + 1, ug_base +  1, s_base + 2))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(t_base + 2 , s_base + 2, ug_base +  3, s_base + 3))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(t_base + 3 , s_base + 3, ug_base +  5, s_base + 4))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(t_base + 4 , s_base + 4, ug_base +  7, s_base + 5))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(t_base + 5 , s_base + 5, ug_base +  9, s_base + 6))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(t_base + 6 , s_base + 6, ug_base + 11, s_base + 7))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(t_base + 7 , s_base + 7, ug_base + 13, s_base + 8))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(t_base + 8 , s_base + 8, ug_base + 15, s_base + 1))

    #Star Facet: 8
    f.write('#Star Facet: num = 8\n')
    f.write('f {:d} {:d} {:d}\n'.format(t_base + 1 , s_base + 2, t_base + 2))
    f.write('f {:d} {:d} {:d}\n'.format(t_base + 2 , s_base + 3, t_base + 3))
    f.write('f {:d} {:d} {:d}\n'.format(t_base + 3 , s_base + 4, t_base + 4))
    f.write('f {:d} {:d} {:d}\n'.format(t_base + 4 , s_base + 5, t_base + 5))
    f.write('f {:d} {:d} {:d}\n'.format(t_base + 5 , s_base + 6, t_base + 6))
    f.write('f {:d} {:d} {:d}\n'.format(t_base + 6 , s_base + 7, t_base + 7))
    f.write('f {:d} {:d} {:d}\n'.format(t_base + 7 , s_base + 8, t_base + 8))
    f.write('f {:d} {:d} {:d}\n'.format(t_base + 8 , s_base + 1, t_base + 1))

    #Upper-Girdle Facet: 16
    f.write('#Upper-Girdle Facet: num = 16\n')
    f.write('f {:d} {:d} {:d}\n'.format(s_base + 2 , ug_base +  1, ug_base +  2))
    f.write('f {:d} {:d} {:d}\n'.format(s_base + 2 , ug_base +  2, ug_base +  3))

    f.write('f {:d} {:d} {:d}\n'.format(s_base + 3 , ug_base +  3, ug_base +  4))
    f.write('f {:d} {:d} {:d}\n'.format(s_base + 3 , ug_base +  4, ug_base +  5))

    f.write('f {:d} {:d} {:d}\n'.format(s_base + 4 , ug_base +  5, ug_base +  6))
    f.write('f {:d} {:d} {:d}\n'.format(s_base + 4 , ug_base +  6, ug_base +  7))

    f.write('f {:d} {:d} {:d}\n'.format(s_base + 5 , ug_base +  7, ug_base +  8))
    f.write('f {:d} {:d} {:d}\n'.format(s_base + 5 , ug_base +  8, ug_base +  9))

    f.write('f {:d} {:d} {:d}\n'.format(s_base + 6 , ug_base +  9, ug_base + 10))
    f.write('f {:d} {:d} {:d}\n'.format(s_base + 6 , ug_base + 10, ug_base + 11))

    f.write('f {:d} {:d} {:d}\n'.format(s_base + 7 , ug_base + 11, ug_base + 12))
    f.write('f {:d} {:d} {:d}\n'.format(s_base + 7 , ug_base + 12, ug_base + 13))

    f.write('f {:d} {:d} {:d}\n'.format(s_base + 8 , ug_base + 13, ug_base + 14))
    f.write('f {:d} {:d} {:d}\n'.format(s_base + 8 , ug_base + 14, ug_base + 15))

    f.write('f {:d} {:d} {:d}\n'.format(s_base + 1 , ug_base + 15, ug_base + 16))
    f.write('f {:d} {:d} {:d}\n'.format(s_base + 1 , ug_base + 16, ug_base +  1))

    #Girdle: 16
    f.write('#Girdle: num = 16\n')
    f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base +  1, lg_base +  1 , lg_base +  2 , ug_base +  2))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base +  2, lg_base +  2 , lg_base +  3 , ug_base +  3))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base +  3, lg_base +  3 , lg_base +  4 , ug_base +  4))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base +  4, lg_base +  4 , lg_base +  5 , ug_base +  5))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base +  5, lg_base +  5 , lg_base +  6 , ug_base +  6))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base +  6, lg_base +  6 , lg_base +  7 , ug_base +  7))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base +  7, lg_base +  7 , lg_base +  8 , ug_base +  8))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base +  8, lg_base +  8 , lg_base +  9 , ug_base +  9))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base +  9, lg_base +  9 , lg_base + 10 , ug_base + 10))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base + 10, lg_base + 10 , lg_base + 11 , ug_base + 11))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base + 11, lg_base + 11 , lg_base + 12 , ug_base + 12))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base + 12, lg_base + 12 , lg_base + 13 , ug_base + 13))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base + 13, lg_base + 13 , lg_base + 14 , ug_base + 14))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base + 14, lg_base + 14 , lg_base + 15 , ug_base + 15))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base + 15, lg_base + 15 , lg_base + 16 , ug_base + 16))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base + 16, lg_base + 16 , lg_base +  1 , ug_base +  1))

    #Lower-Girdle Facet: 16
    f.write('#Lower-Girdle Facet: num = 16\n')
    f.write('f {:d} {:d} {:d}\n'.format(p_base + 2 , lg_base +  1, lg_base +  2))
    f.write('f {:d} {:d} {:d}\n'.format(p_base + 2 , lg_base +  2, lg_base +  3))

    f.write('f {:d} {:d} {:d}\n'.format(p_base + 3 , lg_base +  3, lg_base +  4))
    f.write('f {:d} {:d} {:d}\n'.format(p_base + 3 , lg_base +  4, lg_base +  5))

    f.write('f {:d} {:d} {:d}\n'.format(p_base + 4 , lg_base +  5, lg_base +  6))
    f.write('f {:d} {:d} {:d}\n'.format(p_base + 4 , lg_base +  6, lg_base +  7))

    f.write('f {:d} {:d} {:d}\n'.format(p_base + 5 , lg_base +  7, lg_base +  8))
    f.write('f {:d} {:d} {:d}\n'.format(p_base + 5 , lg_base +  8, lg_base +  9))

    f.write('f {:d} {:d} {:d}\n'.format(p_base + 6 , lg_base +  9, lg_base + 10))
    f.write('f {:d} {:d} {:d}\n'.format(p_base + 6 , lg_base + 10, lg_base + 11))

    f.write('f {:d} {:d} {:d}\n'.format(p_base + 7 , lg_base + 11, lg_base + 12))
    f.write('f {:d} {:d} {:d}\n'.format(p_base + 7 , lg_base + 12, lg_base + 13))

    f.write('f {:d} {:d} {:d}\n'.format(p_base + 8 , lg_base + 13, lg_base + 14))
    f.write('f {:d} {:d} {:d}\n'.format(p_base + 8 , lg_base + 14, lg_base + 15))

    f.write('f {:d} {:d} {:d}\n'.format(p_base + 1 , lg_base + 15, lg_base + 16))
    f.write('f {:d} {:d} {:d}\n'.format(p_base + 1 , lg_base + 16, lg_base +  1))

    #Pavilion Facet: 8
    f.write('#Pavilion Facet: num = 8\n')
    f.write('f {:d} {:d} {:d} {:d} {:d}\n'.format(lg_base +  1 , p_base + 1, c_base + 1, c_base + 2, p_base + 2))
    f.write('f {:d} {:d} {:d} {:d} {:d}\n'.format(lg_base +  3 , p_base + 2, c_base + 2, c_base + 3, p_base + 3))
    f.write('f {:d} {:d} {:d} {:d} {:d}\n'.format(lg_base +  5 , p_base + 3, c_base + 3, c_base + 4, p_base + 4))
    f.write('f {:d} {:d} {:d} {:d} {:d}\n'.format(lg_base +  7 , p_base + 4, c_base + 4, c_base + 5, p_base + 5))
    f.write('f {:d} {:d} {:d} {:d} {:d}\n'.format(lg_base +  9 , p_base + 5, c_base + 5, c_base + 6, p_base + 6))
    f.write('f {:d} {:d} {:d} {:d} {:d}\n'.format(lg_base + 11 , p_base + 6, c_base + 6, c_base + 7, p_base + 7))
    f.write('f {:d} {:d} {:d} {:d} {:d}\n'.format(lg_base + 13 , p_base + 7, c_base + 7, c_base + 8, p_base + 8))
    f.write('f {:d} {:d} {:d} {:d} {:d}\n'.format(lg_base + 15 , p_base + 8, c_base + 8, c_base + 1, p_base + 1))

    #Culet: 1
    f.write('#Culet: num = 1\n')
    f.write('f 57 58 59 60 61 62 63 64\n')

# plot
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')

ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_zlabel("z")

ax.scatter(x, y, z)
#ax.plot(x, y, z)
plt.show()


このpythonコードはカレントディレクトリにtest_diamond.objという3Dオブジェクトファイルを作成します。
幾つかの追加モジュールが必要です。
一応生成したファイルも以下に置いておきます。

g diamond
v  0.275000 -0.000000 0.162000
v  0.194454 -0.194454 0.162000
v  0.000000 -0.275000 0.162000
v  -0.194454 -0.194454 0.162000
v  -0.275000 -0.000000 0.162000
v  -0.194454 0.194454 0.162000
v  -0.000000 0.275000 0.162000
v  0.194454 0.194454 0.162000
v  0.365000 0.151188 0.097200
v  0.365000 -0.151188 0.097200
v  0.151188 -0.365000 0.097200
v  -0.151188 -0.365000 0.097200
v  -0.365000 -0.151188 0.097200
v  -0.365000 0.151188 0.097200
v  -0.151188 0.365000 0.097200
v  0.151188 0.365000 0.097200
v  0.500000 -0.000000 0.000000
v  0.461940 -0.191342 0.000000
v  0.353553 -0.353553 0.000000
v  0.191342 -0.461940 0.000000
v  0.000000 -0.500000 0.000000
v  -0.191342 -0.461940 0.000000
v  -0.353553 -0.353553 0.000000
v  -0.461940 -0.191342 0.000000
v  -0.500000 -0.000000 0.000000
v  -0.461940 0.191342 0.000000
v  -0.353553 0.353553 0.000000
v  -0.191342 0.461940 0.000000
v  -0.000000 0.500000 0.000000
v  0.191342 0.461940 0.000000
v  0.353553 0.353553 0.000000
v  0.461940 0.191342 0.000000
v  0.500000 -0.000000 -0.025000
v  0.461940 -0.191342 -0.025000
v  0.353553 -0.353553 -0.025000
v  0.191342 -0.461940 -0.025000
v  0.000000 -0.500000 -0.025000
v  -0.191342 -0.461940 -0.025000
v  -0.353553 -0.353553 -0.025000
v  -0.461940 -0.191342 -0.025000
v  -0.500000 -0.000000 -0.025000
v  -0.461940 0.191342 -0.025000
v  -0.353553 0.353553 -0.025000
v  -0.191342 0.461940 -0.025000
v  -0.000000 0.500000 -0.025000
v  0.191342 0.461940 -0.025000
v  0.353553 0.353553 -0.025000
v  0.461940 0.191342 -0.025000
v  0.200000 0.082843 -0.283600
v  0.200000 -0.082843 -0.283600
v  0.082843 -0.200000 -0.283600
v  -0.082843 -0.200000 -0.283600
v  -0.200000 -0.082843 -0.283600
v  -0.200000 0.082843 -0.283600
v  -0.082843 0.200000 -0.283600
v  0.082843 0.200000 -0.283600
v  0.012500 0.005178 -0.445225
v  0.012500 -0.005178 -0.445225
v  0.005178 -0.012500 -0.445225
v  -0.005178 -0.012500 -0.445225
v  -0.012500 -0.005178 -0.445225
v  -0.012500 0.005178 -0.445225
v  -0.005178 0.012500 -0.445225
v  0.005178 0.012500 -0.445225
#Table Facet: num = 1
f 1 2 3 4 5 6 7 8
#Bezel Facet: num = 8
f 1 9 17 10
f 2 10 19 11
f 3 11 21 12
f 4 12 23 13
f 5 13 25 14
f 6 14 27 15
f 7 15 29 16
f 8 16 31 9
#Star Facet: num = 8
f 1 10 2
f 2 11 3
f 3 12 4
f 4 13 5
f 5 14 6
f 6 15 7
f 7 16 8
f 8 9 1
#Upper-Girdle Facet: num = 16
f 10 17 18
f 10 18 19
f 11 19 20
f 11 20 21
f 12 21 22
f 12 22 23
f 13 23 24
f 13 24 25
f 14 25 26
f 14 26 27
f 15 27 28
f 15 28 29
f 16 29 30
f 16 30 31
f 9 31 32
f 9 32 17
#Girdle: num = 16
f 17 33 34 18
f 18 34 35 19
f 19 35 36 20
f 20 36 37 21
f 21 37 38 22
f 22 38 39 23
f 23 39 40 24
f 24 40 41 25
f 25 41 42 26
f 26 42 43 27
f 27 43 44 28
f 28 44 45 29
f 29 45 46 30
f 30 46 47 31
f 31 47 48 32
f 32 48 33 17
#Lower-Girdle Facet: num = 16
f 50 33 34
f 50 34 35
f 51 35 36
f 51 36 37
f 52 37 38
f 52 38 39
f 53 39 40
f 53 40 41
f 54 41 42
f 54 42 43
f 55 43 44
f 55 44 45
f 56 45 46
f 56 46 47
f 49 47 48
f 49 48 33
#Pavilion Facet: num = 8
f 33 49 57 58 50
f 35 50 58 59 51
f 37 51 59 60 52
f 39 52 60 61 53
f 41 53 61 62 54
f 43 54 62 63 55
f 45 55 63 64 56
f 47 56 64 57 49
#Culet: num = 1
f 57 58 59 60 61 62 63 64

この内容を test_diamond.obj という名前で保存すればpythonのコードを実行する必要はありません。
あとはBlenderなどの3D編集ツールにインポートすれば良いわけです。
尚、Blenderでインポートする場合がZが上で、法線を外側に揃えて下さい。
マテリアルはグラスBSDFでIORは2.420あたりにセットしてください。
透過屈折するオブジェクトを綺麗にレンダリングするためには周辺環境とライティングを作りこむ必要があるようです。
クオリティティを追求するにはテクが必要ですね。

まだまだ魂が吸い込まれそうな画像には程遠いですが、楽しいものです。
この記事をご覧のあなたもぜひお試しあれ。


コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です