SVGを書くのが面倒なので入力補完できるようにしてみる 3

### 前回

0. [SVGを書くのが面倒なので入力補完できるようにしてみる 1](https://qiita.com/mafumafuultu/items/5184ecdb67066c117233)  
内部に持った状態を変更させるオブジェクト、メソッドチェイン、基本シェイプ、`<path>`のd要素
0. [SVGを書くのが面倒なので入力補完できるようにしてみる 2](https://qiita.com/mafumafuultu/items/fba5a26f0812159e05f0)  
useに潜んでいた罠、`<animate>`
0. SVGを書くのが面倒なので入力補完できるようにしてみる 3
0. [SVGを書くのが面倒なので入力補完できるようにしてみる 4](https://qiita.com/mafumafuultu/items/faaf96f6f4fe3edfb32b)
 
引き続き ↓ コレ作ってるときの話です。
GitHub: [mafumafuultu/svg.js dev](https://github.com/mafumafuultu/svg.js/tree/dev)

### Render 描画品質
　図形、画像、テキストの描画品質を設定できるんですが、プロパティ名は長いし値も長いのでスペルミスとか怖いし、比較に時間をかけるのも嫌なので、プロパティ名とそれに紐づく値も設定できるようにしたいなー。

↑ こんなこと考えたおかげで新しい仕組を考えることになった。
そして、今回はこのRenderでおなかいっぱいになりそうな予感がする。

#### 条件

* 図形、画像、テキストのレンダーの設定を一か所にまとめたい
* レンダーのプロパティ名は長いので補完したい
  * `shape-rendering`
  * `text-rendering`
  * `image-rendering`
* 各レンダープロパティの値も長い。補完したい
  * `optimizeSpeed`
  * `crispEdges`
  * `geometricPrecision`
  * `optimizeLegibility`
  * etc.etc...
* `render().shape().optimizeSpeed().text().geometricPrecision()` な感じで補完したい  
　　↑ 鬼の所業
* 面倒でも可読性は確保したい  
　　↑ 鬼の所業

#### 問題点
まず、この図式

```
Render => Text => Value-A => Render
Render => Shape => Value-B => Render
Render => Image => Value-C => Render
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131781/16d069b9-d699-6150-970e-d08ea7901554.png)

　`Render`を呼び出し、各レンダープロパティ名を補完、各レンダープロパティは設定可能なValueを補完し、`Render`のもとに帰る。

 つまりこんな書き方が成立しなければダメだということになる。

```js
render().Text().auto().Text().auto().Text().auto()
    .Shape().auto().Shape().auto()
    .Image().auto().Text().auto().Image().auto().Text().auto();
```

はてさて、どうしたものか...

#### デカルト「困難は分割せよ」
　まずは理解できる単位に、構造を分解・整理してやればいい。

```
Render => [Text  => Value-A] => Render
Render => [Shape => Value-B] => Render
Render => [Image => Value-C] => Render

Render => [property => Value] => Render

T = [property => Value]
Render => T => Render
```

他人が作ったスパゲッティを読み解くよりは楽です。
見えてくるのは`T`が`Render`の`this`を受け取り、中身を変更して返せばいい。

```js
const __shapeRender = o => ({
    auto() {return o.render['shape-rendering'] = 'auto', o;}
});

const renderGen = () => ({
    render: {'shape-rendering': 'optimizeSpeed'},
    Shape() {return __shapeRender(this);}
});
```

　まぁ、これで `renderGen().Shape().auto().Shape().auto()` はできるようになった。
よしよし、イケるイケる。

### そんなわけなかった

ダサい。

```js
const __shapeRender = o => ({
    auto() {return o.render['shape-rendering'] = 'auto', o;},
    optimizeSpeed() {return o.render['shape-rendering'] = 'optimizeSpeed', o},
    crispEdges() {return o.render['shape-rendering'] = 'crispEdges', o;},
    geometricPrecision() {return o.render['shape-rendering'] ='geometricPrecision', o;},
});
```

* `o.render['shape-rendering']` 何度も書いてる
* `shape-rendering` に 値を入れて`o`を返す構造は共通している
* そして、その構造はほかのrenderにも出てくる

というわけでその辺を解決していく

`renderGen().Shape()` と呼んだ時点で分かっている情報は

* `shape-rendering` に設定される値が後で選ばれる
* `renderGen` の `this` も渡ってくる

なので、こんな仕組みが必要だろう。

```js
const vmod = key => (val, obj) => (obj.render[key] = val, obj);
```
このままでもいいのだけれども、もう少し疎結合にしておく。

```js
// 役割がわかればいいので、頭文字だけにする
const vmod = (k, f) => (v, o) -> (f(o, k, v), o);
const __renderMap = (o, k, v) => o.render[k] = v;
```
　任意の `(o, k, v)` を引数に取る関数を渡せるように変更した。
使うかどうかはわからないけれども、ほかの処理作るときに再利用しやすいようにはなった。
　使用する側は、変更したいプロパティと変更用の関数を`vmod`に渡して、適当な名前を付けて、閉じ込めておき、どの値を使用するか確定したらその値と`renderGen`の`this`を受け取って`render`を変更し、`renderGen`の`this`を returnする。

```js
const __shapeRender = o => ({
    auto() {return this._set('auto', o)},
    _set: vmod('shape-rendering', __renderMap)
});
const renderGen = () => ({
    render: {'shape-rendering': 'optimizeSpeed'},
    Shape() {return __shapeRender(this);}
});
```

これで `renderGen().Shape().auto().Shape().auto();` ができるようになった。
あとは残りのRenderを仕様とにらめっこしながら書いていくだけなので楽なものです。

面倒な割にはこれで80％というところでしょうか？

　次回は、「あったらいいな」「これ、ほしいな」な便利系の整理です。
使い勝手を決める重要な部分ですね。