ezdxfを使ってみよう#3～レイヤー・dxfattribs 図形の属性・描画の補足編～

この章では図形の属性や設定などをまとめます。
[前章はこちら：#2～図面描画編～](https://qiita.com/Rai-see/items/eb74fcbac53f6a749355)
[次章はこちら：#4～寸法編～](https://qiita.com/Rai-see/items/04a5733e77f791cdaa09)
1.   **layer:レイヤーの作成と指定**
1.   **color:色**
1.   **線幅**
1.   **線種**
1.   **点の種類・大きさ**
1.   **文字列の属性**
1.   **ハッチングのパターン**

## layer:レイヤーの作成と指定
### レイヤーの作成
```Python:layers.add
doc.layers.add(name="mylayer", 
color=7, 
linetype="DASHED", 
lineweight =20
)
mylayer.lock() #ロックする
mylayer.unlock() #ロック解除する
mylayer.on() #表示する
mylayer.off() #非表示にする
```
colorやlinetypeなどレイヤーの設定ができます。
後述するdxfattribsと異なり、**"color":7** ではなく**color=7**のように記述する点が注意です。

### レイヤーの指定
図形のレイヤーをdxfattribsで指定します。
```Python:dxfattribs:layer
msp.add_line(start=[0,0], end=[1,1], dxfattribs={'layer':'mylayer'})
```
裏技として作成していないレイヤー名を指定すると、設定が全てデフォルトのレイヤーが作成されて矛盾が解決されます。
「テキトーなレイヤーで良いんだよね」って場合は有効かと。（良いコードかは置いといて）

## color:色

```Python:dxfattribs:color
msp.add_line(start=[0,0], end=[1,1], dxfattribs={"color":2})
```
**Auto Cad Color index (ACI）** というカラーインデックスが基準になっています。
[参考：公式ドキュメント](https://ezdxf.readthedocs.io/en/stable/colors.html#aci-color-values)
[参考：ACIの一覧（英語サイト）](https://gohtx.com/acadcolors.php)

ezdxfを使って図面で一覧を作ってみます。importなどを省略せず全文を記入しますので、良かったらコピペして遊んでみてください(笑)

```Python:color_sample（全文）
import ezdxf
from ezdxf.addons.drawing import matplotlib
from ezdxf.addons.drawing import RenderContext, Frontend
from ezdxf.addons.drawing.matplotlib import MatplotlibBackend
import matplotlib.pyplot as plt

doc = ezdxf.new("R2000")
msp = doc.modelspace()

#ポリラインで図枠を作成
waku_points=[(0,0),(420,0),(420,297),(0,297)]#座標をリストに格納
msp.add_lwpolyline(waku_points, format="xy", close=True, 
dxfattribs={'layer':'mylayer', #レイヤー
'color': 0, #色
'linetype':"Continuous", #線種
'const_width':2 #ポリラインの線幅
})

#forループで文字列とハッチを連続描画
for i in range(256):
  y = -15*(i%20)
  x =  30*(i//20)
  msp.add_text(i, dxfattribs={'layer': 'mylayer','style':'OpenSans-Bold','height':3}).set_pos((6+x, 291+y))
  hatch = msp.add_hatch(color=i)
  hatch.paths.add_polyline_path(
      [(15+x, 295+y), (28+x, 295+y), (28+x, 290+y), (15+x, 290+y)], is_closed=True
      )

#doc.saveas('color_sample.dxf') #dxfに保存する場合コメント解除

fig = plt.figure()
ax = fig.add_axes([0,0,2,2])
ctx = RenderContext(doc)
out = MatplotlibBackend(ax)
Frontend(ctx, out).draw_layout(msp, finalize=True)
fig.show()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/705528/0d739383-d2fc-b3a3-b899-78e1caad38cc.png)

完全に趣味の世界ですが、キレイにできると気持ちいいですね（？）

## 線幅
```Python:dxfattribs:linelineweight
msp.add_line(start=[0,0], end=[1,1], dxfattribs={'lineweight':'20'})
```
ここまでにも出てきました線幅の指定です。CADソフトによって、既定の線幅に変換される場合もあります。

## 線種
### 線種の指定 ###
```Python:dxfattribs:linetype
msp.add_line(start=[0,0], end=[1,1], dxfattribs={'linetype':"PHANTOMX2" })
```
### 線種の追加 ###
```Python:linetypes.add
doc.linetypes.add(
        name="DOTTED_TEST", #名称
        pattern=[0.2, 0.0, -0.2],#要素の感覚などのパターン
        description="Dotted_test .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  ."#表示される例
      )
msp.add_line((0, 0), (5, 0), #始点・終点
dxfattribs={'layer':'mylayer', #レイヤー
'color': 2, #色
'lineweight':10, #線幅
'linetype':"DOTTED_TEST" #線種
})
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/705528/cbd3c390-14ff-4644-2e3e-5a3be29b0eb5.png)

patternに以下の数値を組み合わせて、新しい線種をデザインできます。
・n>0　：長さnの直線
・n=0.0：点
・n<0　：長さnの空白

[公式ドキュメント](https://ezdxf.readthedocs.io/en/stable/tutorials/linetypes.html#tutorial-for-complex-linetypes)には複雑な指定や、線種の一括作成方法など紹介されていますが、非AutoCADユーザーは使えなかったり、線種はsetup=Trueで十分だと思うのでので割愛します。
線の中に文字を取り込んだり、図形と取り込んだりもできるそうです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/705528/bd227ac8-d8bd-9c73-7ecd-f4332b0ead0e.png)
こんな感じ

setup=Trueに設定されている線種の種類については [#1：linetypes](https://qiita.com/Rai-see/items/aebf4587f7a9b30e3ab1#linetypes%E7%B7%9A%E7%A8%AE%E3%81%AF21%E7%A8%AE%E9%A1%9E)を参照下さい。

## 点の種類・大きさ 
ここに関しては環境的にうまく表現できないので軽く紹介します。点の種類は公式ドキュメントを参照ください。
```Python:point header
#dxfのヘッダー部
doc.header['$PDMODE'] = 65 #点の種類 #✖matplotlib
doc.header['$PDSIZE'] = 10 #点の大きさ #✖matplotlib
#dxfのエンティティ部
msp.add_point([1, 1], 
dxfattribs={'layer':'mylayer', #レイヤー
'color': 3, #色
})
```
[参考：点の種類・大きさ　公式ドキュメント](https://ezdxf.readthedocs.io/en/stable/dxfentities/point.html?highlight=point#point)

## 文字列の属性
### 文字列のdxfattribsについて ###
以下の通り、これらを使ったコードは[#1:Text 文字列の描画](https://qiita.com/Rai-see/items/eb74fcbac53f6a749355#text-%E6%96%87%E5%AD%97%E5%88%97%E3%81%AE%E6%8F%8F%E7%94%BB)にまとめてありますので参照してください。

- 'height':15, #文字高
- 'width':0.5, #文字幅　割合で書く
- 'rotation':45, #角度
- 'oblique':15, #傾斜 ✖matplotlib
- 'color':4 #色

### フォントの追加 ###
日本語含むフォントは以下のように追加できます。
matplotlibがうまく機能せず結果が確認できない場合などあり、CADで確認する必要があります。
```Python:styles.add
doc.styles.add("MSゴシック", font="msgothic.ttc")
msp = doc.modelspace()
msp.add_text("日本語は美しい", dxfattribs={"style": "MSゴシック"})
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/705528/6e324acd-630e-13ed-2a13-19e6ed91cd72.png)

### 文字の位置・配置 15種類
文字の位置と配置は若干文法が異なります。
**set_pos**と**align**を追加してコードを書きます。

```Python:set_pos & align
msp.add_text('TEXT',dxfattribs={None}) #可読性のためNoneにしてあります
.set_pos((1,1), #配置基準点
align='MIDDLE_CENTER' #配置位置
)
```
まだわかり難いですので、配置の種類を確認しましょう。
### 文字列の基本的な配置 ###
- BOTTOM_LEFT 
- BOTTOM_CENTER
- BOTTOM_RIGHT
- MIDDLE_LEFT
- MIDDLE_CENTER
- MIDDLE_RIGHT
- TOP_LEFT
- TOP_CENTER
- TOP_RIGHT
- LEFT
- CENTER
- RIGHT

forループで書き出してみます
```Python:alignments_1
alignments_dic = {1:'BOTTOM_LEFT',2:'BOTTOM_CENTER',3:'BOTTOM_RIGHT',4:'MIDDLE_LEFT',5:'MIDDLE_CENTER',6:'MIDDLE_RIGHT',7:'TOP_LEFT',8:'TOP_CENTER', 9:'TOP_RIGHT',10:'LEFT', 11:'CENTER', 12:'RIGHT'}
for i, alignment in enumerate(alignments_dic.values()):

  x = (i % 3)*5
  y = (i // 3)*3
  msp.add_text(alignment,
              dxfattribs={
                  'style': 'LiberationSerif',
                  'height': 0.15,
                  'color': 4}
              ).set_pos((2+x, 6+y), align=alignment)
  msp.add_line(start=[0+x, 6+y], end=[4+x, 6+y])
  msp.add_line(start=[2+x, 5+y], end=[2+x, 7+y])
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/705528/ec9a8e5c-f730-e55c-e16e-8331ef44580b.png)

**alignは.set_posで指定した基準点との位置関係**になります。TOP_LEFTだと基準点の左上に文字が来るのではなく、文字の左上が基準点になります。
CADで作図しているとあたりまえなのですが、コーディングしているときはピンときづらいかもしれません。

### 文字列の特殊な配置 ###
set_posに２点の文字の基準点を指示し、その領域に文字を配置します。

- ALIGNED　文字の縦横比を固定して拡縮されます
- FIT　文字の高さが固定され拡縮されます
- MIDDLE 特殊な配置メソッドですが、結果はMIDDLE_CENTERと同じとの事。紹介は割愛します

```Python:alignments_2
alignments = {13:'ALIGNED', 14:'FIT'}
for i, alignment in enumerate(alignments.values()):

  msp.add_text(alignment+str(12345),
              dxfattribs={
                  'style': 'LiberationSerif',
                  'height': 0.15,
                  'color': 4}
              ).set_pos((2+5*i, 6),(4+5*i, 8), align=alignment)
  msp.add_line(start=[0+5*i, 6], end=[4+5*i, 6])
  msp.add_line(start=[2+5*i, 5], end=[2+5*i, 7])
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/705528/f596e8e2-eb75-972d-50f0-8e212613ed89.png)

上記の特殊な配置はCADによって反映されない場合が確認されました。作業効率面でもdxfattribsで'rotation'（角度）を指定するのが正直良いかなと思います。

### alignment辞書 一応置いときますね(´･ω･`) ／ ###
自分で使う用に…。作図例コードのようにalignmentは辞書化しておくのがいいかなと思ってます。1~9まではキーボードのテンキーと同じ配置ですので、**格ゲー勢に優しい辞書**です。
2:'BOTTOM_CENTER'(中下)　9:'TOP_RIGHT'（右上）など
```Python:alignments_dic
alignments_dic = {1:'BOTTOM_LEFT',2:'BOTTOM_CENTER',3:'BOTTOM_RIGHT',4:'MIDDLE_LEFT',5:'MIDDLE_CENTER',6:'MIDDLE_RIGHT',7:'TOP_LEFT',8:'TOP_CENTER', 9:'TOP_RIGHT','LEFT':'LEFT', 'CENTER':'CENTER', 'RIGHT':'RIGHT', 'ALIGNED':'ALIGNED', 'FIT':'FIT', 'MIDDLE':'MIDDLE'}
```


[参考：文字列の配置　公式ドキュメント](https://ezdxf.readthedocs.io/en/stable/tutorials/text.html#tutorial-for-text)

## ハッチングのパターン 71種類 ##

前章で触れたハッチングのパターンをまとめます。
公式ドキュメントでもパターン名がテキスト化されていないので、ここでテキスト化して一緒に紹介します。
こちらも全文を記入しますので、パターンのコピペ素材としてもご活用ください。

<details><summary>ハッチングパターン一覧</summary>

・ANSI31
・ANSI32
・ANSI33
・ANSI34
・ANSI35
・ANSI36
・ANSI37
・ANSI38
・ACAD_ISO02W100
・ACAD_ISO03W100
・ACAD_ISO04W100
・ACAD_ISO05W100
・ACAD_ISO06W100
・ACAD_ISO07W100
・ACAD_ISO08W100
・ACAD_ISO09W100
・ACAD_ISO10W100
・ACAD_ISO11W100
・ACAD_ISO12W100
・ACAD_ISO13W100
・ACAD_ISO14W100
・ACAD_ISO15W100
・ANGLE
・AR-B816
・AR-B816C
・AR-B88
・AR-BRELM
・AR-BRSTD
・AR-CONC
・AR-HBONE
・AR-PARQ1
・AR-RROOF
・AR-RSHKE
・AR-SAND
・BOX
・BRASS
・BRICK
・BRSTONE
・CLAY
・CORK
・CROSS
・DASH
・DOLMIT
・DOTS
・EARTH
・ESCHER
・FLEX
・GOST_GLASS
・GOST_WOOD
・GOST_GROUND
・GRASS
・GRATE
・GRAVEL
・HEX
・HONEY
・HOUND
・INSUL
・LINE
・MUDST
・NET
・NET3
・PLAST
・PTAST1
・SACNCR
・SRUARE
・STARS
・STEEL
・SWAMP
・TRANS
・TRIANG
・ZIGZAG
</details>

```Python:hatch pattern（全文）
import ezdxf
from ezdxf.addons.drawing import matplotlib
from ezdxf.addons.drawing import RenderContext, Frontend
from ezdxf.addons.drawing.matplotlib import MatplotlibBackend
import matplotlib.pyplot as plt

doc = ezdxf.new("R2000")
msp = doc.modelspace()
#ハッチングパターンのリスト
patterns =['ANSI31', 'ANSI32', 'ANSI33', 'ANSI34', 'ANSI35', 'ANSI36', 'ANSI37', 'ANSI38', 'ACAD_ISO02W100',
 'ACAD_ISO03W100', 'ACAD_ISO04W100', 'ACAD_ISO05W100', 'ACAD_ISO06W100', 'ACAD_ISO07W100', 'ACAD_ISO08W100',
 'ACAD_ISO09W100', 'ACAD_ISO10W100', 'ACAD_ISO11W100', 'ACAD_ISO12W100', 'ACAD_ISO13W100', 'ACAD_ISO14W100',
 'ACAD_ISO15W100', 'ANGLE', 'AR-B816', 'AR-B816C', 'AR-B88', 'AR-BRELM', 'AR-BRSTD', 'AR-CONC', 'AR-HBONE',
 'AR-PARQ1', 'AR-RROOF', 'AR-RSHKE', 'AR-SAND', 'BOX', 'BRASS', 'BRICK', 'BRSTONE', 'CLAY', 'CORK', 'CROSS',
 'DASH', 'DOLMIT', 'DOTS', 'EARTH', 'ESCHER', 'FLEX', 'GOST_GLASS', 'GOST_WOOD', 'GOST_GROUND', 'GRASS',
 'GRATE', 'GRAVEL', 'HEX', 'HONEY', 'HOUND', 'INSUL', 'LINE', 'MUDST', 'NET', 'NET3', 'PLAST', 'PTAST1',
 'SACNCR', 'SRUARE', 'STARS', 'STEEL', 'SWAMP', 'TRANS', 'TRIANG', 'ZIGZAG']
#alignment辞書の宣言
alignments_dic = {1:'BOTTOM_LEFT',2:'BOTTOM_CENTER',3:'BOTTOM_RIGHT',4:'MIDDLE_LEFT',5:'MIDDLE_CENTER',6:'MIDDLE_RIGHT',7:'TOP_LEFT',8:'TOP_CENTER', 9:'TOP_RIGHT','LEFT':'LEFT', 'CENTER':'CENTER', 'RIGHT':'RIGHT', 'ALIGNED':'ALIGNED', 'FIT':'FIT', 'MIDDLE':'MIDDLE'}

for i, pattern in enumerate(patterns):
  hatch = msp.add_hatch()
  x = i%10 * 15
  y = i//10 * -15 

  msp.add_text(pattern,
            dxfattribs={
                'style': 'LiberationSerif',
                'height': 1,
                'color': 0}
            ).set_pos( (0+x, 10+y), align=alignments_dic[1])

  hatch.paths.add_polyline_path(
      [(0+x, y), (10+x, y), (10+x, 10+y), (0+x, 10+y)], is_closed=True
      )
  hatch.set_pattern_fill(pattern, scale=0.5, color=i%10)

#doc.saveas('hatch_sample.dxf') #dxfに保存する場合コメント解除

fig = plt.figure()
ax = fig.add_axes([0,0,3,3])
ctx = RenderContext(doc)
out = MatplotlibBackend(ax)
Frontend(ctx, out).draw_layout(msp, finalize=True)
fig.show()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/705528/8ff2ddff-bc3a-af62-39c8-6dccbbd239d7.png)

ああ美しい…。とても美しいですね（？）

[参考：ハッチングのパターン　公式ドキュメント](https://ezdxf.readthedocs.io/en/stable/tutorials/hatch.html#predefined-hatch-pattern)
***
大まかなものは紹介できたと思いますので、次章は寸法についてまとめます。
寸法にはさらに独自のdxfattribsが用いられますので、混同を避けるためこういう構成にしました。
寸法が作図できれば立派な設計図が描画できるようになりそうですね。

https://qiita.com/Rai-see/items/aebf4587f7a9b30e3ab1

https://qiita.com/Rai-see/items/eb74fcbac53f6a749355

https://qiita.com/Rai-see/items/294a36b84ae13eb1077f

https://qiita.com/Rai-see/items/04a5733e77f791cdaa09

https://qiita.com/Rai-see/items/51dcea8f496b58c41989

https://qiita.com/Rai-see/items/713b106a058da50d7d43