ContentAlignmentとStringAlignmentの相互変換

# 背景

LabelのTextを自力でDrawしたかったんです。
OnPaintの中でDrawStringすればオッケです。
簡単でしょ？

# 詳細

なるべく、Labelコントロールのプロパティを尊重したいです。
特にTextAlignプロパティは重要でした。

ところで、Label.TextAlign は ContentAlignment 型のプロパティです。
えーと、こんな感じ

![image](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/136132/7f43e9c5-37fa-1f06-e8df-232af6254dc2.png)

しかし、DrawStringで表示位置を制御するにはStringAlignment型のアトリビュート二つ、つまり

* Alignment
* LineAlignment

の組み合わせで、縦横の位置を指定しなければなりません。
つまりAlignmentは、この位置を表していて

![image](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/136132/e918b884-719e-0c6f-cbb1-3a4b162a2bba.png)

LineAlignmentは、この位置を表していて

![image](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/136132/a420a5bc-7187-3ee4-36ef-3d52f4776f73.png)

この組み合わせで、ContentAlignmentと同じ位置指定ができるようになっている。
ちゅー訳です。
> NearとFarの位置関係は日本語ではの話、と思ってね

# 分析

ところで、ContentAlignment型はenumで以下の値を持っています。

```js

TopLeft = 1
TopCenter = 2
TopRight = 4
MiddleLeft = 16
MiddleCenter = 32
MiddleRight = 64
BottomLeft = 256
BottomCenter = 512
BottomRight  =1024
```

StringAlignment型もenumで以下の値。

```js
Near = 0
Center = 1
Far = 2
```

<br/>
因みに、ContentAlignmentを図示すると以下の形。

![image](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/136132/daf6d99c-4e6a-6110-f8de-9308977199f9.png)

enumの値は実際に立ったビットが２進数値としてどう評価されるかの値。
<br/>
一方、StringAlignmentは下図。

![image](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/136132/96babbf0-7d05-6e26-b2d3-aed266217cce.png)

enum値はただの、値を持った識別子。

# 変換 その１（あっち ⇒ こっち）

<span style="font-size:10px; color:red;">えーと、まずは分かり易そうな方から…</span>


StringAlignmentはただの値、と云いながらもよ～く見てみると以下の対応関係が成り立つようになっています。

![image](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/136132/ca5b4d15-3c32-13fd-67ba-f09c40651d6f.png)

Left  = Near なのでContentAlignment（の一部）とStringAlignmentの位置関係は、上の図のようになります。
で、着目すべきは赤い数字。

つまり、$2^{StringAlignment}$とすると、その結果が１だったり２だったり４だったりして、ContentAlignmentの対応位置にビットを立てる事ができるって訳です。
<br/>
次は垂直方向の位置合わせです。
サクッと図にしてしまうと以下のような感じです。

![image](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/136132/296bdd58-a6e1-b9a5-6861-38b46e08959f.png)

Top / Middle / Bottom其々を４ビットのブロックとして考えると、StringAlignmentの値とブロックの位置関係を対応付ける事が出来ます。
つまり、水平位置の変換結果に垂直位置の変換値を掛け合わせる事で、ContentAlignmentに変換されます。
こう云う事―

```csharp
ContentAlignment = Math.Pow(2, Alignment) * Math.Pow(16, LineAlignment);
```

わっかり難ーい！
と云う時には以下の様に考えても可。

$2^{Alignment}$ で求めた水平配置情報を―

* ０ブロック左シフトすると、垂直方向にはTopに配置される
* １ブロック左シフトすると、垂直方向にはMiddleに配置される
* ２ブロック左シフトすると、垂直方向にはBottomに配置される

```csharp
ContentAlignment = Math.Pow(2, Alignment) << (4 * LineAlignment);
```

４ビット左シフトすると云う事は、（２の４乗）＝１６を掛けるってのと同義だからね。
<br/>
さて―
簡単な方は片付いたけど、本来やりたかったのはContentAlignmentを二つのStringAlignemntに分割する方でした。

# 変換 その２（こっち ⇒ あっち）

あっちからこっちへの変換は数式でできたので、こっちからあっちへの逆変換も数式で出来る筈です。
数ⅡBを思い出しましょう！

冪乗の逆関数は対数になります。
$2^n = x$ なら、$n = log_2 x$ と云う事です。

|配置|冪乗|対数|
|---|---|---|
|left |$2^0 = 1$ |$0 = log_2 1$ |
|center |$2^1 = 2$ |$1 = log_2 2$ |
|right |$2^2 = 4$ |$2 = log_2 4$ |
<br/>
さて、もう一回この図を持ってきて逆の見方で読み解くと－

![image](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/136132/ca5b4d15-3c32-13fd-67ba-f09c40651d6f.png)

もし ContentAlignment の値が $1$ ならば、$log_2 1 = 0$ で、(Topの)Nearです。
もし ContentAlignment の値が $2$ ならば、$log_2 2 = 1$ で、(Topの)Middleになります。
もし ContentAlignment の値が $4$ ならば、$log_2 4 = 2$ で、(Topの)Farに変換できます。

じゃぁ、垂直方向がTop以外の場合はどうなるかと云うと、この場合も先ほどとは逆に４ビット単位で右シフトしてあげれば良いですね。

ここも例に依って対数です。
一つのブロックが４ビット（$=2^4=16$）単位ですので $log_{16} ContentAlignment$ とすれば、ビットがどのブロックに含まれているかを知る事が出来ます。

えーと、ここにきて更に公式ですけど…
C#のMathライブラリにおいて扱えるのは、常用対数か自然対数のどちらか[^1]です。（対数の底が $10$ か $e$ かって事）
上に出てきた様に底を $2$とか $16$ にしたければ以下の変換公式を使う必要があります。

$log_x n = log_{10} n / log_{10} x$

ここら辺を考慮すると縦位置はー

```C#
LineAlignment = Math.Floor(Math.Log10(ContentAlignment) / Math.Log10(16))
```

横位置の方は縦位置に従って右シフトした上で対数変換すれば…

```C#
Alignment = Math.Log10(ContentAlignment >> (4 * LineAlignment)) / Math.log10(2)
```

で、縦横のStringAlignmentに変換できました。

# じゃぁ、C#で表現してみましょう

多分、Extensionにした方が使い易い様な気がする…
ちょっと癖が出てしまいましたが、双方向ともStringFormatに対する拡張メソッドになります。

```C#
public static class ExtClass {
    public static ContentAlignment ToContentAlignment(this StringFormat Me) {
        return (ContentAlignment)((int)Math.Pow(2, (int)Me.Alignment) << (4 * (int)Me.LineAlignment));
    }
    
    public static void SetStringAlignment(this StringFormat Me, ContentAlignment ca) {
        int Valignment = (int)Math.Floor(Math.Log10((int)ca) / Math.Log10(16));
        int Halignment = (int)(Math.Log10((int)ca >> (4 * Valignment)) / Math.Log10(2));

        Me.LineAlignment = (StringAlignment)Valignment;
        Me.Alignment = (StringAlignment)Halignment;
    }
}
```

```C#:使い方
    StringFormat sf = new StringFormat();
    Console.WriteLine(sf.ToContentAlignment());            //  sfのAlignmentをContentAlignmentで取得

    sf.SetStringAlignment(ContentAlignment.BottomCenter);  //  ContentAlignemnetからsfにAlignmentをコピー
    Console.WriteLine(sf.ToContentAlignment());
```

てな感じで…

[^1]: .NET Frmeworkの場合