例えばこのような形のちょっと変わったハートのつくり方を紹介します!!
![](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F986141%2Ff724f0fa-f3b5-56a7-b329-b679bdcd4b71.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=7e3ad4505936dab1990c2adb728b3e3e)
王道のハート
ハートの関数はこちらにたくさん紹介されていました。
一番バランスの良いハートは、このような三角関数の組み合わせでつくることができます!
x = 16 * np.sin(t)**3
y = 13 * np.cos(t) - 5 * np.cos(2 * t) - 2 * np.cos(3 * t) - np.cos(4 * t)
![](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F986141%2F7f5c7e4a-52f2-3980-c2ef-cf6d10537538.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=66b9b5087b63a449fda72e46c9016335)
コード全体はこちら
# ハートの関数
def heart_shape(num_points):
t = np.linspace(0, 2 * np.pi, num_points)
x = 16 * np.sin(t)**3
y = 13 * np.cos(t) - 5 * np.cos(2 * t) - 2 * np.cos(3 * t) - np.cos(4 * t)
return x, y
# プロットの設定
num_points = 1000
x, y = heart_shape(num_points)
# 描画
fig = make_subplots(rows=1, cols=1)
heart_trace = go.Scatter(
x=x,
y=y,
mode='lines',
line=dict(color='red'),
name='Heart Shape'
)
fig.add_trace(heart_trace)
# レイアウト
x_range = [min(x), max(x)]
y_range = [min(y), max(y)]
range_max = max(max(x_range), max(y_range))
range_min = min(min(x_range), min(y_range))
fig.update_layout(
xaxis=dict(
scaleanchor="y",
scaleratio=1,
showgrid=False,
zeroline=False,
range=[range_min, range_max]
),
yaxis=dict(
showgrid=False,
zeroline=False,
range=[range_min, range_max]
),
showlegend=False
)
fig.show()
3Dのきれいなハート
3Dのつるっとしたハートも描くことができます。
![](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F986141%2F00ea4d9a-b0e4-7531-1047-ebb342ac8f2c.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=b0c748505c33e3e1ee7b5c4f5c487988)
コードはこちら
def points(x):
sl = ()
for i in range(x.ndim):
x = (x[sl + np.index_exp[:-1]] + x[sl + np.index_exp[1:]]) / 2.0
sl += np.index_exp[:]
return x
# ハートの関数
n = 100
xr = np.linspace(-1.3, 1.3, n)
yr = np.linspace(-1.3, 1.3, n)
zr = np.linspace(-1.3, 1.3, n)
xr, yr, zr = np.meshgrid(xr, yr, zr)
x = points(xr)
y = points(yr)
z = points(zr)
heart = (x**2 + 2.5*y**2 + z**2 - 1)**3 - x**2 * z**3 - (1/9)*y**2 * z**3
# 頂点と面
verts, faces, _, _ = measure.marching_cubes(heart, level=0)
# 3Dメッシュ
x, y, z = verts.T
i, j, k = faces.T
fig = go.Figure(data=[go.Mesh3d(
x=x,
y=y,
z=z,
i=i,
j=j,
k=k,
color='red',
opacity=0.5
)])
# レイアウト
fig.update_layout(
scene=dict(
xaxis=dict(showbackground=False),
yaxis=dict(showbackground=False),
zaxis=dict(showbackground=False)
)
)
fig.show()
メッシュなので頂点を多くした方が滑らかです。
ちょっと変わったハート
初めに紹介した2Dのハートのz軸を調整することで面白い形をつくることができます。
動きのあるハート
左右非対称のハートです。
![](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F986141%2F4554685e-d78e-abc6-1723-c98c8d6afa01.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=13fc91a2ed69370eb1d09cf99dd7285b)
sinカーブを使った滑らかな曲線でz軸の高低差を使ってy軸の正負でサイズが違って見えるように描けます。
ハートの真ん中の部分は実はクルッと回転しています。
コードはこちら
# ハートの関数
def heart_coordinates(num_points):
t = np.linspace(0, 2 * np.pi, num_points)
x = 16 * np.sin(t)**3
y = 13 * np.cos(t) - 5 * np.cos(2 * t) - 2 * np.cos(3 * t) - np.cos(4 * t)
z = -np.sin(t)
return np.vstack((x, y, z)).T
# 描画の設定
num_points = 1000
pos_3d = heart_coordinates(num_points)
node_trace = go.Scatter3d(
x=pos_3d[:, 1],
y=pos_3d[:, 0],
z=pos_3d[:, 2],
mode='markers',
marker=dict(size=5, color='red')
)
fig = make_subplots(rows=1, cols=1, specs=[[{'type': 'scatter3d'}]])
fig.add_trace(node_trace)
# レイアウト
fig.update_layout(
title="Heart Shape Graph using Plotly",
showlegend=False,
scene=dict(
xaxis=dict(showbackground=False),
yaxis=dict(showbackground=False),
zaxis=dict(showbackground=False),
camera=dict(
eye=dict(x=0, y=0, z=2) #アングル調整が大事
)
)
)
fig.show()
つながっていないハート
こちらはz軸方向に伸びる紐のような形状です。
![](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F986141%2Fa2fe4f0a-6528-b003-7b7b-b46e00783b6b.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=cb2a1370c684e29f2a7545a3b1bb64ae)
コードはこちら
# ハートの関数
def heart_coordinates(num_points):
t = np.linspace(0, 2 * np.pi, num_points)
x = 16 * np.sin(t)**3
y = 13 * np.cos(t) - 5 * np.cos(2 * t) - 2 * np.cos(3 * t) - np.cos(4 * t)
z = t
return np.vstack((x, y, z)).T
# 描画の設定
num_points = 1000
pos_3d = heart_coordinates(num_points)
node_trace = go.Scatter3d(
x=pos_3d[:, 1],
y=pos_3d[:, 0],
z=pos_3d[:, 2],
mode='markers',
marker=dict(size=3, color='red')
)
fig = make_subplots(rows=1, cols=1, specs=[[{'type': 'scatter3d'}]])
fig.add_trace(node_trace)
# レイアウト
fig.update_layout(
title="Heart Shape Graph using Plotly",
showlegend=False,
scene=dict(
xaxis=dict(showbackground=False,showaxeslabels=False),
yaxis=dict(showbackground=False,showaxeslabels=False),
zaxis=dict(showbackground=False,showaxeslabels=False),
camera=dict(
eye=dict(x=-0.01, y=0, z=2.2)
)
)
)
fig.show()
これを応用して、z軸が大きくなるにつれてx,y軸も大きくすればこのような図形をつくることができます。
![](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F986141%2Ff724f0fa-f3b5-56a7-b329-b679bdcd4b71.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=7e3ad4505936dab1990c2adb728b3e3e)
コードはこちら
# ハートの関数
def heart_coordinates(num_points):
t = np.linspace(0, 10 * np.pi, num_points) #2n*piで何周するかを定義
z = t
x = 16 * np.sin(t)**3*z
y = (13 * np.cos(t) - 5 * np.cos(2 * t) - 2 * np.cos(3 * t) - np.cos(4 * t))*z
return np.vstack((x, y, z)).T
# 描画の設定
num_points = 5000
pos_3d = heart_coordinates(num_points)
node_trace = go.Scatter3d(
x=pos_3d[:, 1],
y=pos_3d[:, 0],
z=pos_3d[:, 2],
mode='markers',
marker=dict(size=3, color='red')
)
fig = make_subplots(rows=1, cols=1, specs=[[{'type': 'scatter3d'}]])
fig.add_trace(node_trace)
# レイアウト
fig.update_layout(
title="Heart Shape Graph using Plotly",
showlegend=False,
scene=dict(
xaxis=dict(showbackground=False,showaxeslabels=False),
yaxis=dict(showbackground=False,showaxeslabels=False),
zaxis=dict(showbackground=False,showaxeslabels=False),
camera=dict(
eye=dict(x=-0.01, y=0, z=2.2)
)
)
)
fig.show()
おまけ
尖ったハート
sin→cosにすると左右対称の中心で尖るハートができます。
![](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F986141%2F0a18e4a9-ab30-372e-e7be-345c070bf651.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=df09c63cfc7ad0c5c023fa535ee2370b)
コードはこちら
# ハートの関数
def heart_coordinates(num_points):
t = np.linspace(0, 2 * np.pi, num_points)
x = 16 * np.sin(t)**3
y = 13 * np.cos(t) - 5 * np.cos(2 * t) - 2 * np.cos(3 * t) - np.cos(4 * t)
z = np.cos(t)
return np.vstack((x, y, z)).T
# 描画の設定
num_points = 1000
pos_3d = heart_coordinates(num_points)
node_trace = go.Scatter3d(
x=pos_3d[:, 1],
y=pos_3d[:, 0],
z=pos_3d[:, 2],
mode='markers',
marker=dict(size=5, color='red'),
)
fig = make_subplots(rows=1, cols=1, specs=[[{'type': 'scatter3d'}]])
fig.add_trace(node_trace)
# レイアウト
fig.update_layout(
title="Heart Shape Graph using Plotly",
showlegend=False,
scene=dict(
xaxis=dict(showbackground=False),
yaxis=dict(showbackground=False),
zaxis=dict(showbackground=False),
camera=dict(
eye=dict(x=0, y=0, z=2)
)
)
)
fig.show()