ちょっと変わったハートの関数

**例えばこのような形のちょっと変わったハートのつくり方を紹介します!!**

<img width="100%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/986141/f724f0fa-f3b5-56a7-b329-b679bdcd4b71.png">


## 王道のハート
ハートの関数はこちらにたくさん紹介されていました。

https://mathworld.wolfram.com/HeartCurve.html

一番バランスの良いハートは、このような三角関数の組み合わせでつくることができます！
```python
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)
```
<img width="100%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/986141/7f5c7e4a-52f2-3980-c2ef-cf6d10537538.png">

<details><summary>コード全体はこちら</summary>

```python
# ハートの関数
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()
```

</details>

## 3Dのきれいなハート
3Dのつるっとしたハートも描くことができます。

<img width="100%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/986141/00ea4d9a-b0e4-7531-1047-ebb342ac8f2c.png">

<details><summary>コードはこちら</summary>

```python
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)

# ３Dメッシュ
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()

```

</details>

メッシュなので頂点を多くした方が滑らかです。

## ちょっと変わったハート
初めに紹介した2Dのハートのz軸を調整することで面白い形をつくることができます。

### 動きのあるハート
左右非対称のハートです。

<img width="100%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/986141/4554685e-d78e-abc6-1723-c98c8d6afa01.png">

sinカーブを使った滑らかな曲線でz軸の高低差を使ってy軸の正負でサイズが違って見えるように描けます。
ハートの真ん中の部分は実はクルッと回転しています。
![無題の動画 (2).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/986141/935b70fc-c117-1dc5-0e4f-7f86c09946ad.gif)

<details><summary>コードはこちら</summary>

```python
# ハートの関数
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()
```

</details>

### つながっていないハート

こちらはz軸方向に伸びる紐のような形状です。

<img width="100%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/986141/a2fe4f0a-6528-b003-7b7b-b46e00783b6b.png">

![無題の動画 (3).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/986141/76be71da-46bc-d11f-84e2-ccb6d3e4560b.gif)

<details><summary>コードはこちら</summary>

```python
# ハートの関数
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()
```

</details>

これを応用して、z軸が大きくなるにつれてx,y軸も大きくすればこのような図形をつくることができます。

<img width="100%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/986141/f724f0fa-f3b5-56a7-b329-b679bdcd4b71.png">

![無題の動画 (4).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/986141/bc5eb813-5ba8-1603-fe90-db6d3224e041.gif)

<details><summary>コードはこちら</summary>

```python
# ハートの関数
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()
```

</details>

## おまけ
### 尖ったハート
sin→cosにすると左右対称の中心で尖るハートができます。

<img width="100%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/986141/0a18e4a9-ab30-372e-e7be-345c070bf651.png">

![無題の動画 (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/986141/aa5ecc23-06cb-dae5-86d6-5649090f44ff.gif)

<details><summary>コードはこちら</summary>

```python
# ハートの関数
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()
```

</details>