MATLABで遊ぼう：簡単に時計を作れるか

#前書き
前回はマインスイーパの作り方を紹介したが、今回は時計を簡単に作れるかを考えてみる。
頑張れば時計の模様（円や数字など）もかけることはできるが、ここでは、画像として導入したケースを紹介する。その理由は、作成者オリジナルの時計模様画像に一般化できるためである。

#環境
MATLABのR2019aバージョン
Windows10
MATLAB Toolbox: Image Processing Toolbox

#全体のフロー
プログラム作成のフロー：
１．オリジナルの時計画像を作成
２．時計の中心や針などを定義
３．時間と共に時計の針の角度計算や位置更新

### オリジナルの時計画像を作成
図１のような簡単な画像を作成した。今回紹介する事例では、作成者オリジナルの時計画像に対応するようにしているが、条件として、円形の時計のみが対象となる。
![BaseWatch3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484040/3aec63e7-fea7-d22b-48c2-d12f288e9831.jpeg)
図１：著作者オリジナルの時計画像

### 時計の中心や針などを定義
まずは、MATLABのimfindcircles関数を利用して上記オリジナル画像よりその中心を求める。
以下コードではMATLABのビルトイン関数IMFINDCIRCLESで画像から中心を求めているが（汎用性の高度化を目的で）、
基本的に、画像ごと、中心の位置をメタデータとして記録しておけば、そのまま取り組んでも大丈夫。
次は、その中心から円周方向に伸びる３本の針（時、分、秒）を定義する。
時と分の針を時計らしく太めにするため、二本の直線で表現する。
これらの二本の直線は円の中心側でお互い隔ており、円周側で一体になっている。
コードは以下になる（コメントもご参照）：

``` MATLAB
% パラメータ
offst = 1;
rH  = 60;
rM  = 80;
rS  = 110;
radrange = [100 180];

% 自作のオリジナル画像を取り組む
I = imread('BaseWatch3.jpg');%図１の画像はこのファイルにある
I = flipud(I);
[c,r] = imfindcircles(I,radrange);%画像中心計算
%画像の取り組み
figure('ToolBar','none','menubar','none')
hI = imshow(I); %画像表示
set(gca,'YDir','normal') %画像表示におけるY軸の方向を通常の直交座標に合わせる
hold on
plot(c(1),c(2),'k.','Markersize',15) %中心に例えば黒い丸を描く
%針の定義・初期化
hlH1= line([c(1) c(1)],[c(2) c(2)], 'Color','Blue', 'Linewidth',2);%時の針⓵
hlH2= line([c(1) c(1)],[c(2) c(2)], 'Color','Blue', 'Linewidth',2);%時の針⓶
hlM1= line([c(1) c(1)],[c(2) c(2)], 'Color','Red', 'Linewidth',1);%分の針⓵
hlM2= line([c(1) c(1)],[c(2) c(2)], 'Color','Red', 'Linewidth',1);%分の針⓶
hlS = line([c(1) c(1)],[c(2) c(2)], 'Color','Black', 'Linewidth',1);%秒の針
```

### 時間と共に時計の針の角度計算や位置更新
MATLABで簡単に現在時刻を読み込んだり、その中から、時、分や秒の値を分離することができる。
この値に応じて、それぞれの針の位置を更新すればいい。
それぞれの針は、片方（円中心側）の位置が常に円の中心にあるので、求めるべきことは、それぞれの針の円周側の位置（X,Y座標）がどうなるかとのことである。

そのため下記ワークフローを考える:
（１）時、分や秒について、その現在値に基づいてそれぞれの針の角度を求める。
このため、以下の関数getAngles()をご参照。

（２）求めた角度と針の長さ情報を利用して、円柱座標から直交座標に変換によって、針の円周側の位置を求める（X,Y座標）。

（３）それぞれの針の新規位置に関する表示を更新する
最後に、Whileループの中に上記（１）～（３）を繰り返していけば、時計の出来上がりである。

※ちなみに、今回は40行以下で済んだとのこと。

``` MATLAB
while isvalid(hI)
    %時、分、秒の値を分離
    hms = datestr(now,'hh-MM-ss');
    hr  = str2double(hms(1:2));
    if hr > 12
        hr = hr - 12;
    end
    mn  = str2double(hms(4:5));
    ss  = str2double(hms(7:8));
    
    %時間の針の処理
    [aH,aM,aS]  = getAngles(hr,mn,ss); 
    [x, y] = pol2cart(aH,rH);
    adjustPosition(c,x,y,[hlH1,hlH2],offst+1);
    %分の針の処理
    [x, y] = pol2cart(aM,rM);
    adjustPosition(c,x,y,[hlM1,hlM2],offst);
    %秒の針の処理
    [x, y] = pol2cart(aS,rS);
    adjustPosition(c,x,y,hlS,offst);
end
```

### getAngles()関数
``` MATLAB
function [aH,aM,aS]  = getAngles(hr,mn,ss)
aH = deg2rad(90 - hr*30 - mn*0.5 - ss*(1/120));
aM = deg2rad(90 - mn*6 - ss*(1/10));
aS = deg2rad(90 - ss*6);
end
```

### adjustPosition()関数
``` MATLAB
function adjustPosition(c,x,y,hndls,offst)
p   = [c(1)+x c(2)+y];
if numel(hndls)>1
    set(hndls(1),'XData',[c(1)-offst p(1)],'YData',[c(2)-offst p(2)]);
    set(hndls(2),'XData',[c(1)+offst p(1)],'YData',[c(2)+offst p(2)]);
else
    set(hndls,'XData',[c(1) pS(1)],'YData',[c(2) pS(2)]);
end
drawnow
end
```

最後に出来上がるのが、こんな時計。シンプルではあるものの、MATLABでとても簡単に作れるたのがうれしい。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484040/f0ccbcf8-04e5-1be0-8697-044247238226.png)