[Blender2.8]blender.orgのBMesh Operators (bmesh.ops)のサンプルを試して基本をおさえる。

##ドキュメントへのリンク
2019年7月時点でのblender.orgドキュメントのサンプルを読んでいく。
https://docs.blender.org/api/blender2.8/bmesh.ops.html

コードから生成されるのは下記。
<img width="300" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/346396/144d1f74-806f-ce65-0690-c4968d4e0fff.png">

##コード全体
```python
# This script uses bmesh operators to make 2 links of a chain.

import bpy
import bmesh
import math
import mathutils

# Make a new BMesh
bm = bmesh.new()

# Add a circle XXX, should return all geometry created, not just verts.
bmesh.ops.create_circle(
    bm,
    cap_ends=False,
    radius=0.2,
    segments=8)


# Spin and deal with geometry on side 'a'
edges_start_a = bm.edges[:]
geom_start_a = bm.verts[:] + edges_start_a
ret = bmesh.ops.spin(
    bm,
    geom=geom_start_a,
    angle=math.radians(180.0),
    steps=8,
    axis=(1.0, 0.0, 0.0),
    cent=(0.0, 1.0, 0.0))
edges_end_a = [ele for ele in ret["geom_last"]
               if isinstance(ele, bmesh.types.BMEdge)]
del ret


# Extrude and create geometry on side 'b'
ret = bmesh.ops.extrude_edge_only(
    bm,
    edges=edges_start_a)
geom_extrude_mid = ret["geom"]
del ret


# Collect the edges to spin XXX, 'extrude_edge_only' could return this.
verts_extrude_b = [ele for ele in geom_extrude_mid
                   if isinstance(ele, bmesh.types.BMVert)]
edges_extrude_b = [ele for ele in geom_extrude_mid
                   if isinstance(ele, bmesh.types.BMEdge) and ele.is_boundary]
bmesh.ops.translate(
    bm,
    verts=verts_extrude_b,
    vec=(0.0, 0.0, 1.0))


# Create the circle on side 'b'
ret = bmesh.ops.spin(
    bm,
    geom=verts_extrude_b + edges_extrude_b,
    angle=-math.radians(180.0),
    steps=8,
    axis=(1.0, 0.0, 0.0),
    cent=(0.0, 1.0, 1.0))
edges_end_b = [ele for ele in ret["geom_last"]
               if isinstance(ele, bmesh.types.BMEdge)]
del ret


# Bridge the resulting edge loops of both spins 'a & b'
bmesh.ops.bridge_loops(
    bm,
    edges=edges_end_a + edges_end_b)


# Now we have made a links of the chain, make a copy and rotate it
# (so this looks something like a chain)

ret = bmesh.ops.duplicate(
    bm,
    geom=bm.verts[:] + bm.edges[:] + bm.faces[:])
geom_dupe = ret["geom"]
verts_dupe = [ele for ele in geom_dupe if isinstance(ele, bmesh.types.BMVert)]
del ret

# position the new link
bmesh.ops.translate(
    bm,
    verts=verts_dupe,
    vec=(0.0, 0.0, 2.0))
bmesh.ops.rotate(
    bm,
    verts=verts_dupe,
    cent=(0.0, 1.0, 0.0),
    matrix=mathutils.Matrix.Rotation(math.radians(90.0), 3, 'Z'))

# Done with creating the mesh, simply link it into the scene so we can see it

# Finish up, write the bmesh into a new mesh
me = bpy.data.meshes.new("Mesh")
bm.to_mesh(me)
bm.free()


# Add the mesh to the scene
obj = bpy.data.objects.new("Object", me)
bpy.context.collection.objects.link(obj)

# Select and make active
bpy.context.view_layer.objects.active = obj
obj.select_set(True)
```

## 円の生成
<img width="300"  src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/346396/9b2d8696-4d2b-3653-798f-d87dc158913b.png">


```python
# Make a new BMesh
bm = bmesh.new()

# Add a circle XXX, should return all geometry created, not just verts.
bmesh.ops.create_circle(
    bm,
    cap_ends=False,
    radius=0.2,
    segments=8)
```
ops.xxxに、生成したBMesh(ここではbm)を渡して、BMeshを更新していくのが基本。上記の画像では円が実際に画面に表示されているが、ObjectとMeshを生成して、それをシーンにリンクする必要がある。コードの最後の方の処理。

##円からチューブの生成
<img width="300" alt="Screen Shot 2019-07-09 at 14.36.42.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/346396/3af0592d-ba8f-393f-556c-5240902c4426.png">

```python
# Spin and deal with geometry on side 'a'
edges_start_a = bm.edges[:]
geom_start_a = bm.verts[:] + edges_start_a
ret = bmesh.ops.spin(
    bm,
    geom=geom_start_a,
    angle=math.radians(180.0),
    steps=8,
    axis=(1.0, 0.0, 0.0),
    cent=(0.0, 1.0, 0.0))
edges_end_a = [ele for ele in ret["geom_last"]
               if isinstance(ele, bmesh.types.BMEdge)]
del ret
```
円を回転して、半周のチューブを生成。頂点の配列と、エッジの配列をひとつの配列につなげて、それをジオメトリのデータとして渡すという特徴的な処理があるが、エッジの配列だけをgeomに渡しても頂点とエッジが生成され、エラーも特に出てこない。bmesh.ops.spinから返ってくるのは、geom_lastのみをキーに持つdictionary。

## 最初に作ったサークルを引き伸ばす
<img width="300px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/346396/f9367332-6665-049a-fa52-cb91bb11b368.png">
Blenderで、最初に作ったサークルを選択して、E > G する処理。

```python
# Extrude and create geometry on side 'b'
ret = bmesh.ops.extrude_edge_only(
    bm,
    edges=edges_start_a)
geom_extrude_mid = ret["geom"]
del ret
```
伸ばす処理。Blenderで、頂点を選択してEを押した直後の状態。

```python
# Collect the edges to spin XXX, 'extrude_edge_only' could return this.
verts_extrude_b = [ele for ele in geom_extrude_mid
                   if isinstance(ele, bmesh.types.BMVert)]
edges_extrude_b = [ele for ele in geom_extrude_mid
                   if isinstance(ele, bmesh.types.BMEdge) and ele.is_boundary]
bmesh.ops.translate(
    bm,
    verts=verts_extrude_b,
    vec=(0.0, 0.0, 1.0))
```

引き伸ばした頂点を実際にZ軸方向に1.0移動する。

## 反対側にチューブを半周させる
<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/346396/a8024443-d458-5250-1110-5a1469db2502.png">


```python
# Create the circle on side 'b'
ret = bmesh.ops.spin(
    bm,
    geom=verts_extrude_b + edges_extrude_b,
    angle=-math.radians(180.0),
    steps=8,
    axis=(1.0, 0.0, 0.0),
    cent=(0.0, 1.0, 1.0))
edges_end_b = [ele for ele in ret["geom_last"]
               if isinstance(ele, bmesh.types.BMEdge)]
del ret
```
伸ばした先のエッジからスピン。

## くっつける
<img width="300" alt="Screen Shot 2019-07-09 at 14.42.23.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/346396/155bb600-d9c1-25d2-0dc1-318b90ff13c5.png">

```python
# Bridge the resulting edge loops of both spins 'a & b'
bmesh.ops.bridge_loops(
    bm,
    edges=edges_end_a + edges_end_b)
```
edges_end_aは、下の半周チューブを生成したときに格納している。これで1つ目が完成。

## 輪っかを複製する

```python
# Now we have made a links of the chain, make a copy and rotate it
# (so this looks something like a chain)

ret = bmesh.ops.duplicate(
    bm,
    geom=bm.verts[:] + bm.edges[:] + bm.faces[:])
geom_dupe = ret["geom"]
verts_dupe = [ele for ele in geom_dupe if isinstance(ele, bmesh.types.BMVert)]
del ret
```
輪っかを複製して、verts_dupeに、複製された頂点群を格納。

## 複製したメッシュを移動

<img width="300" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/346396/144d1f74-806f-ce65-0690-c4968d4e0fff.png">


```python
# position the new link
bmesh.ops.translate(
    bm,
    verts=verts_dupe,
    vec=(0.0, 0.0, 2.0))
bmesh.ops.rotate(
    bm,
    verts=verts_dupe,
    cent=(0.0, 1.0, 0.0),
    matrix=mathutils.Matrix.Rotation(math.radians(90.0), 3, 'Z'))
```

##オブジェクトとメッシュを生成し、シーンに追加して表示する

```python
me = bpy.data.meshes.new("Mesh")
bm.to_mesh(me)
bm.free()

# Add the mesh to the scene
obj = bpy.data.objects.new("Object", me)
bpy.context.collection.objects.link(obj)

# Select and make active
bpy.context.view_layer.objects.active = obj
obj.select_set(True)
```
これまでに制作したBMeshからMeshを作り出し、そのMeshを持つオブジェクトを生成する。
最後の2行は、オブジェクトを選択する処理。

##感想
BMeshそのものは参照渡しで更新。返り値で、端っこの部分・複製されたものだけのように都合が良いものを返してくれるのが素敵。