連立方程式を使ってプログラムを書く話

あるプログラムを書いていて、

> 指定の枠(mm)に、指定の解像度(dpi)で
> ヨコ幅(pt)、タテ幅(pt)のサイズがわかっている画像を置く場合、
> その画像に必要な解像度(dpi)が知りたい

ということがあった。
![ss_2020-11-01_11_07_31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956/924a0469-d4a0-79d7-8002-58ffaaa8b464.png)

これを計算してくれる関数を、 javascript で作りたい。

### 画像の縦幅と横幅、どちらを比較するか？

これは、画像枠と画像の比率を比較してフラグにすればいいので

```js
var frameMmW = 80; // 枠ヨコ幅
var frameMmH = 60; // 枠タテ幅
var imagePtW = 142; // 画像ヨコ幅
var imagePtH = 64; // 画像タテ幅
```

とすると、

```js
// 画像の[横/縦]が枠の[横/縦]より大きければ、横幅を使用する
var useWidth = (imagePtW / imagePtH >= frameMmW / frameMmH);
```

みたいな感じに書ける。

ちょっと [RunJS](https://runjs.dev/) を使って試してみる。
[RunJS - A playground for JavaScript and TypeScript](https://runjs.dev/)

![ss 2020-11-01 11.29.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956/f5ad98da-9caa-19c9-2c5d-fef8683e65f6.png)

試しに、画像のヨコ幅とタテ幅を変えてみると、
画像枠にタテを合わせたいサイズでちゃんと `false` になる。

![ss 2020-11-01 11.33.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956/a4cdb399-e9ac-a063-341a-7de4b41f8a2e.png)

### mm と pt を inch に再計算する

単位 `mm` と `pt` だと比較しづらい。
`dpi` は `dot per inch` なので、それぞれ `inch` に直す。

72ポイント = 1インチ = 25.4ミリ だから……。

```js
var unitToInch = function (size, unit) {
    var perUnit = {
        'in': 1,
        'pt': 72,
        'mm': 25.4
    };
    return size / perUnit[unit];
}

var frameInW = unitToInch(frameMmW, 'mm');
var frameInH = unitToInch(frameMmH, 'mm');
var imageInW = unitToInch(imagePtW, 'pt');
var imageInH = unitToInch(imagePtH, 'pt');
```

![ss 2020-11-01 12.09.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956/786ac82a-69b7-d602-e58c-cac54f292280.png)

Adobe のソフトで見ても、上手く計算できているように見える。

![ss 2020-11-01 12.08.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956/d0998d8d-755b-571c-b5ee-57346bde099c.png)

### 画像の解像度を出す

さて、画像の解像度を出すとして、

`dpi` は `dot per inch` だから

```
ピクセル数 ÷ 幅（inch） = 解像度 (dpi)
```

ということは

```
枠のピクセル数 ÷ 枠のサイズ（inch） = 枠の解像度 (dpi)
画像のピクセル数 ÷ 画像のサイズ（inch） = 画像の解像度 (dpi)
```

で、 **二次連立方程式** の形になる。

枠のピクセル数 = 画像のピクセル数
となるようにしたいわけだから、

```
画像or枠のピクセル数 ÷ 枠のサイズ（inch） = 枠の解像度 (dpi)
画像or枠のピクセル数 ÷ 画像のサイズ（inch） = 画像の解像度 (dpi)
```

ええと、両辺にサイズをかけて

```
画像or枠のピクセル数 = 枠の解像度 (dpi) × 枠のサイズ（inch）
画像or枠のピクセル数 = 画像の解像度 (dpi) × 画像のサイズ（inch）
```

左辺が同じになったから、下を左辺に、上を右辺にして

```
画像の解像度 (dpi) × 画像のサイズ（inch）= 枠の解像度 (dpi) × 枠のサイズ（inch）
```

欲しいのは画像の解像度だから、両辺を画像のサイズで割れば

```
画像の解像度 (dpi) = 枠の解像度 (dpi) × 枠のサイズ（inch）÷ 画像のサイズ (inch)
```

できた。これを関数に落とし込む。

```js
var getImageDpi = function getImageDpi(frameDpi, frameInchSize, imageInchSize) {
  return frameDpi * frameInchSize / imageInchSize;
}
```

えっこんな簡単な計算？
試してみる。

```js

/**
 * 他の単位のサイズをインチに直す
 * @param {number} size - サイズ
 * @param {string} unit - 単位(in,pt,mm)
 * @return {number} - インチサイズ
 */
var unitToInch = function (size, unit) {
    var perUnit = {
        'in': 1,
        'pt': 72,
        'mm': 25.4
    };
    return size / perUnit[unit];
}

/**
 * 画像のdpiを計算する
 * @param {number} frameDpi - 枠の解像度 (dpi)
 * @param {number} frameInchSize - 枠のサイズ (inch)
 * @param {number} imageInchSize - 画像のサイズ (inch)
 * @return {number} - 画像の解像度 (dpi)
 */
var getImageDpi = function getImageDpi(frameDpi, frameInchSize, imageInchSize) {
  return frameDpi * frameInchSize / imageInchSize;
}

var frameDpi = 300; // 枠の解像度
var frameMmW = 80; // 枠ヨコ幅
var frameMmH = 60; // 枠タテ幅
var imagePtW = 142; // 画像ヨコ幅
var imagePtH = 64; // 画像タテ幅

// 幅と高さどちらを使うか
var useWidth = (imagePtW / imagePtH >= frameMmW / frameMmH);
// 枠のinchサイズ
var frameInchSize = unitToInch((useWidth? frameMmW: frameMmH), 'mm');
// 画像のinchサイズ
var imageInchSize = unitToInch((useWidth? imagePtW: imagePtH), 'pt');
// 画像の解像度
var imageDpi = getImageDpi(frameDpi,frameInchSize,imageInchSize);
console.log('imageDpi: ' + imageDpi);
```

![ss 2020-11-01 12.50.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956/125bcb44-aac3-5ee1-3064-b3897d5e2795.png)

約 **479dpi** と出た。
Photoshopで画像を開けて、解像度を479dpiで再サンプル実行

![ss_2020-11-01_13_13_54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956/03de11df-9e90-7a8a-e04e-98f8e4cdeab4.png)

保存後、再サンプルなしで、幅を80mmに変えてみる。

![ss_2020-11-01_13_15_52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956/d25f6583-cc11-94b5-9ad3-af007c74a5c0.png)

ピクセル数が変わるのでぴったりとはいかないが、
だいたいOKみたい。

### まとめ

こんな感じで連立方程式を解く感じで、
自分が欲しい機能の関数を書くことができます。

自分の場合、どうしても欲しい関数が（あちこちググっても）見つからない場合、

1. 公式をググる
2. 現状取れるパラメータを、公式にあてはめて方程式を作る
3. 欲しいパラメータが左辺に来るように式を解く
4. 関数に落とし込む

とかもやります。アフィン変換系とか。

以上、プログラムと連立方程式の話でした。