新案デジアナ時計

 私が初めてデジタル時計に接したのは、中学一年生の頃でした。リーフ式と言って、数字の描かれた小さな板(リーフ)が、スロー回転のモーターと歯車によって1分毎にカタっとめくれる時計でした。デジタル時計とはいっても、当時のそれはLED(液晶ディスプレイ)を使っておらず、時または分を表す数字の板を、モーターと歯車が家庭の交流式電源でゆっくりと回転して、機械的に一枚一枚めくっていくアナログ的なものでした。それでも、当時は、時計といえば、丸か四角の文字盤に長針と短針が回って動くアナログ時計が一般的だったので、上の機械式デジタル時計は意外と斬新に思われていました。
 それに、それなりの趣(おもむき)があったと思います。カタカタ時計とも呼ばれ、1分経過すると、カタっと自然に音がして数字が変わります。デジタルとは言っても、その仕組みは多分にアナログ的だったのです。
 そのようなことを踏まえて、デジタル時計とアナログ時計のどちらがどれくらい優れているのかということを私は考えてみました。結局は、どちらも一長一短があると思います。デジタル時計は、数字そのものの文字情報で時刻がわかります。言わば、人間の左脳を主に刺激すると言えましょう。一方、アナログ時計は、長針と短針の組み合わせの位置と形で、時刻がわかります。その感覚的なイメージ情報が、人間の右脳を主に刺激すると言えましょう。いずれの方式の時計も時刻が瞬時にわかりますが、人間の脳への刺激が微妙に異なっているとも言えると思います。
 現在では、時刻表示のスペースがある場合は、デジタルとアナログを併用した時計がよく使われています。それが、現在のところ一番理想的な方式であるようです。例えば、日常テレビや車などで時刻以外の情報も重要な場合は、デジタル時計がよく使われます。その一方で、私たちは、お腹が空(す)いた時などに瞬時に感覚的に時刻を知りたい場合は、壁などにかかったアナログ時計を無意識に見てしまうような気がします。そうしたそれぞれの場合のために、デジタルとアナログが併用された時計があると、その時の状況に合わせて、どちらの方式の時計を見たらいいかを選べる。そういうメリットがあると思います。
 私が今回新たに考案した時計は、デジタルとアナログの融合型です。デジタルとアナログの併用型の時計は、それぞれの時計が並んだり混在したりします。それに比べて、デジタルとアナログを渾然一体(こんぜんいったい)として区別がつかないようなものにしてしまおうというのが私のアイデアなのです。
 実は、現在の私のプログラミング環境では、グラフィックス関係のプログラミングがあまり自由にできません。パソコンのハードウェア・ソフトウェア面での進歩・発展は著しく、そのモデルチェンジあるいはバージョンアップ毎に古いものが使えなくなってゆく傾向にありました。その経験を踏まえて、私は、グラフィックスのライブラリやパッケージソフトなどの新たな追加に頼りたくないと考えるようになりました。なるべくウィンドウズOSの標準システムで、プログラミングできないかと考えるようになりました。しかしながら、作成するプログラムによっては、どうしてもグラフィックス的な表現が必要になることも少なくありません。そのため、グラフィックス関係のソフトウェアの追加なしでグラフィックスっぽい表現ができないかと考えることとなりました。
 そのヒントは、アスキーアートにありました。私がそれを見たのは、かなり古く、小学生の頃に、当時のコンピュータが打ち出した年賀状の活字印刷でした。それは、戌(いぬ)年に担任の先生が送って下さった年賀状でしたが、犬の絵を文字だけの活字でプリントしていました。その先生の手書きの書面によると、電子計算機に年賀状を入れて打ち出した、とされていました。小学生の私は、当時は実際に見たことも触れたこともない『電子計算機(コンピュータ)』というものをスゴいと思いました。
 あれから時はだいぶ流れて、今日(こんにち)では、CRTや液晶ディスプレイの画面上に四角いウィンドウを作ることができます。そのウィンドウ内では、文字をどんな位置にでも表示することができます。アスキーアートのように、昔のコンピュータで紙面に順次プリントするものから、だいぶやり方が変わってきました。けれども、文字だけで、絵などのイメージを表現するという根本的な考え方は、昔も今も変わっていないと思います。
 すなわち、私は、アナログ時計の文字盤と長針と短針をすべて数字で表現することができると考えました。これが、デジタルとアナログを融合した時計、すなわち、デジアナ時計というわけです。長針は、現在の時刻の分を表す数字を並べて作ります。また、短針は、現在の時刻の時を表す数字を並べて作ります。すると、何時何分かがぱっと見て、数字でもわかるし、長針短針の位置と形でもわかる時計ができるのです。
 こうして述べていくと、大したプログラムではないことがわかると思います。よって、以下にウィンドウズOSのパソコンで動くVBスクリプトのプログラムリストを示します。(これと同じ動作をするJスクリプトもおまけに作りました。参考までに、付録プログラムリストとして追記しました。)


デジアナ時計.hta

<STYLE type="text/css">
          SPAN  {position:absolute;}
          #pnl1 {color:white; font-size:24pt;}
          #pnl2 {color:red; font-size:24pt;}
          #sbr  {font-size:18pt; color:cyan;font-weight:bold;}
          #lbr  {font-size:10pt; color:yellow;}
</STYLE>

<HTA:Application Id=oHTA Border=dialog Scroll=no MaximizeButton=no Contextmenu=no />


<HEAD>
   <TITLE>デジアナ時計</TITLE>

<SCRIPT LANGUAGE="VBScript">
Option Explicit

Const OrgX = 168  ' 文字盤の中心(横軸方向)
Const OrgY = 170  ' 文字盤の中心(縦軸方向)
Const pnlcnt = 12 ' 文字盤の上限数字
Const lbrcnt = 8  ' 長針(分)の文字数
Const sbrcnt = 3  ' 短針(時)の文字数

Dim mode, svSig, pseudo


Sub Window_OnLoad()

    Call ResizeTo(370, 410)
    Call drawShortLongHands()
    Call drawPanel()
    Call SetInterval("drawShortLongHands", 1000)

End Sub


Sub Document_OnDblClick()

    If mode = 0 Then
       mode = 1
       Call ChangeToMono()
       Document.Title = "デジアナ時計(モノトーン版)"

    ElseIf mode = 1 Then
       Call ClearInterval()
       mode = 2
       Set pseudo = New TimeForTestClass
       svSig = pseudo.Signal()
       Document.Title = "デジアナ時計テストパターン"
       Call drawShortLongHands()
       Call SetInterval("drawShortLongHands", 100)

    Else
       Call ClearInterval()
       mode = 0
       Set pseudo = Nothing
       Document.Title = "デジアナ時計"
       Call drawShortLongHands()
       Call ChangeToColor()
       Call SetInterval("drawShortLongHands", 1000)
    End If

End Sub

Private Sub ChangeToMono()
    Dim idx

    For idx = 0 to pnlcnt - 1
        pnl2(idx).style.visibility = "hidden"
    Next

    For idx = 0 to sbrcnt - 1
        sbr(idx).style.color = "white"
    Next

    For idx = 0 to lbrcnt - 1
        lbr(idx).style.color = "white"
    Next

End Sub


Private Sub ChangeToColor()
    Dim idx

    For idx = 0 to pnlcnt - 1
        pnl2(idx).style.visibility = "visible"
    Next

    For idx = 0 to sbrcnt - 1
        sbr(idx).style.color = "cyan"
    Next

    For idx = 0 to lbrcnt - 1
        lbr(idx).style.color = "yellow"
    Next

End Sub


Sub drawShortLongHands()
    Dim nowh, nowm, add, ang, bar, badj, idx, sig

    If mode = 2 Then
       nowh = pseudo.Hour()
       nowm = Pseudo.Minute()

       sig = pseudo.Signal()
       If sig <> svSig Then
          svSig = sig
          If sig Then
             Call ChangeToColor()
          Else
             Call ChangeToMono()
          End If
       End If

    Else
       nowh = CInt(Hour(Time))
       nowm = CInt(Minute(Time))
    End If

    If nowh >= 12 Then nowh = nowh - 12

    add = nowm \ 12
    ang = nowh * 30 + 270 + add * 6
    If ang >= 360 Then ang = ang - 360

    If nowh >= 10 Then
       bar = 25
       badj = 25
    Else
       bar = 20
       badj = 20
    End If

    For idx = 0 to sbrcnt - 1

        Call  drawDigi(bar, ang, nowh, sbr, idx, 0, 0)
        bar = bar + badj
    Next

    ang = nowm * 6 + 270
    If ang >= 360 Then ang = ang - 360

    bar = 10

    For idx = 0 to lbrcnt - 1

        Call  drawDigi(bar, ang, nowm, lbr, idx, 6, 10)
        bar = bar + 16
    Next

End Sub


Sub drawPanel()
    Dim bar, ang, idx

    bar = 150
    ang = 300
    For idx = 0 to pnlcnt - 1

        Call  drawDigi(bar, ang, -1, pnl1, idx, 0, 0)
        Call  drawDigi(bar, ang, -1, pnl2, idx, -5, -5)

        ang = ang + 30
        If ang = 360 Then ang = 0

    Next

End Sub


Private Sub drawDigi(bar, ang, num, br, idx, ofsx, ofsy)
    Dim rad, x, y

    rad = Round(ang * 3.14 / 180, 2)
    x = Round(bar * Cos(rad)) + OrgX + ofsx
    y = Round(bar * Sin(rad)) + OrgY + ofsy
    br(idx).style.left = x
    br(idx).style.top = y
    If num > -1 Then
       br(idx).innerText = Cstr(num)
    End If

End Sub


Class TimeForTestClass
    Private Hcnt, Mcnt, sflag

    Private Sub Class_Initialize

        Hcnt = 0
        Mcnt = -1
        sflag = False

    End Sub

    Property Get Signal()

        If Hcnt = 0 And Mcnt = 0 Then sflag = Not sflag

        Signal = sflag

    End Property

    Property Get Hour()

        Hour = Hcnt

    End Property

    Property Get Minute()

        Mcnt = Mcnt + 1
        If Mcnt > 59 Then

           Mcnt = 0
           Hcnt = Hcnt + 1
           If Hcnt > 11 Then

              Hcnt = 0
           End If           
        End If

        Minute = Mcnt        

    End Property

End Class

</SCRIPT>

</HEAD>

<BODY  bgcolor="indigo" scroll=no>

    <SCRIPT LANGUAGE="VBScript">
        Dim idx, str

        str = ""
        For idx = 1 to pnlcnt
            str = str + "<SPAN id=pnl2>" + Cstr(idx) + "</SPAN>"
            str = str + "<SPAN id=pnl1>" + Cstr(idx) + "</SPAN>"
        Next

        For idx = 1 to lbrcnt
            str = str + "<SPAN id=lbr></SPAN>"
        Next

        For idx = 1 to sbrcnt
            str = str + "<SPAN id=sbr></SPAN>"
        Next

        Document.write(str)

    </SCRIPT>

</BODY>


 今回のプログラムは、ウィンドウの文字盤をダブルクリックすることによって、カラー表示モード・モノトーン表示モード・テストパターンモードの3つのモードに切り替えることができるようなっています。カラー表示モードでは、少しだけ凝ったデザインにしてみました。モノトーン表示モードでは、比較的シンプルな時計のデザインをねらってみました。テストパターンモードは、アナログ時計で表される時刻を短時間で全て試して、文字盤と短針・長針の数字の重なり具合をチェックできるようにしてあります。数字が重なって読みづらくなる場合がないかどうかをテストするために作りました。
 プログラムの原理的なことを言うならば、時刻を取り出す関数をプログラムの中で使っています。コンピュータ上で作る時計プログラムは、中味はデジタル時計であることが多いことがわかります。画面上でいくらアナログ時計を作っても、中味はデジタルであることは明白です。つまり、パソコン画面上のアナログ時計は、いくらアナログっぽく見えても、本質的にはデジタルなのです。この記事の冒頭で述べた、昔のデジタル時計とは、全く正反対の性質のものだということがわかります。私が新たに考案したデジアナ時計も、基本的には今のデジタル時計と同じものなのかもしれません。
 ただし、今回の時計プログラムのアナログ表現が全く意味がなかったかというと、そうでもないような気がします。というのは、文字盤に円状に配置する数字のXY座標位置や、時刻をアナログ時計の短針・長針として配置するXY座標位置を得るために、三角関数を使っています。Sin(サイン)やCos(コサイン)の関数をプログラム上で利用する良い練習例になると思います。デジタル時計とアナログ時計とを結ぶものがSinやCosの三角関数だということは、当たり前と言ってしまえばそれまでです。けれども、そんなことは中学や高校でも習ったことがなかったので、そんな私には極めて新鮮に思われました。三角関数自体は高校などで学んだ記憶はありますが、あれから40年以上も経った今になって、時計プログラムを作るのに役に立つとは思いませんでした。(ただし、一銭にもなりませんが…。)


付録プログラムリスト
デジアナ時計js.hta

<STYLE type="text/css">
          SPAN  {position:absolute;}
          #pnl1 {color:white; font-size:24pt;}
          #pnl2 {color:red; font-size:24pt;}
          #sbr  {font-size:18pt; color:cyan;font-weight:bold;}
          #lbr  {font-size:10pt; color:yellow;}
</STYLE>

<HTA:Application Id=oHTA Border=dialog Scroll=no MaximizeButton=no Contextmenu=no />


<HEAD>
   <TITLE>デジアナ時計 Jスクリプト版</TITLE>

<SCRIPT LANGUAGE="JScript">

var mode, svSig;


var TimeForTestClass = function() {

    var Hcnt = 0;
    var Mcnt = -1;
    var sflag = false;

    this.Signal = function () {

        if(Hcnt == 0 && Mcnt == 0) sflag = !sflag;

        return sflag;
    }

    this.Hour = function () { return Hcnt; }

    this.Minute = function () {

        Mcnt += 1;
        if(Mcnt > 59) {

           Mcnt = 0;
           Hcnt += 1;
           if(Hcnt > 11) Hcnt = 0;
        }
        return Mcnt;
    }    
}

var pseudo = new TimeForTestClass();

window.onload = function () {

    resizeTo(370, 410);
    mode = 0;
    drawShortLongHands();
    drawPanel();
    setInterval("drawShortLongHands()", 1000);
}


document.ondblclick = function () {

    if(mode == 0) {

       mode = 1;
       changeToMono();
       document.title = "デジアナ時計(モノトーン版)";
    }
    else if(mode == 1) {

       clearInterval();
       mode = 2;
       svSig = pseudo.Signal();
       document.title = "デジアナ時計テストパターン";
       drawShortLongHands();
       setInterval("drawShortLongHands()", 100);
    }
    else {

       clearInterval();
       mode = 0;
       pseudo = null;
       pseudo = new TimeForTestClass();
       document.title = "デジアナ時計 Jスクリプト版";
       drawShortLongHands();
       changeToColor();
       setInterval("drawShortLongHands()", 1000);
    }
}


function changeToMono() {

    for(i=0;i<pnlcnt;i++) pnl2[i].style.visibility = "hidden";

    for(i=0;i<sbrcnt;i++) sbr[i].style.color = "white";
 
    for(i=0;i<lbrcnt;i++) lbr[i].style.color = "white";
}


function changeToColor() {

    for(i=0;i<pnlcnt;i++) pnl2[i].style.visibility = "visible";

    for(i=0;i<sbrcnt;i++) sbr[i].style.color = "cyan";
 
    for(i=0;i<lbrcnt;i++) lbr[i].style.color = "yellow";
}


var OrgX = 168    // 文字盤の中心(横軸方向)
var OrgY = 170    // 文字盤の中心(縦軸方向)
var pnlcnt = 12;  // 文字盤の上限数字
var lbrcnt = 8    // 長針(分)の文字数
var sbrcnt = 4;   // 短針(時)の文字数

function drawShortLongHands() {
    var nowh, nowm, bar, badj

    if(mode == 2) {

       nowh = pseudo.Hour();
       nowm = pseudo.Minute();
       var sig = pseudo.Signal();

       if(sig != svSig) {

          svSig = sig;
          if(sig)
             changeToColor();
          else
             changeToMono();
       }
    }
    else{

       var now = new Date();

       nowh = now.getHours();
       nowm = now.getMinutes();
    }

    if(nowh >= 12) nowh -= 12;

    var add = Math.floor(nowm / 12);
    var ang = nowh * 30 + 270 + add * 6;

    if(ang >= 360) ang -= 360;

    if(nowh >= 10) {
       bar = 25;
       badj = 25;
    }
    else {
       bar = 20;
       badj = 20;
    }

    for(i=0;i<sbrcnt;i++) {

        drawDigi(bar, ang, nowh, sbr, i, 0, 0);
        bar += badj;
    }

    ang = nowm * 6 + 270;

    if(ang >= 360) ang -= 360;

    bar = 10;

    for(i=0;i<lbrcnt;i++) {

        drawDigi(bar, ang, nowm, lbr, i, 6, 10);
        bar += 16;
    }
}


function drawPanel() {

    var bar = 150;
    var ang = 300;

    for(i=0;i<pnlcnt;i++) {

        drawDigi(bar, ang, -1, pnl1, i, 0, 0);
        drawDigi(bar, ang, -1, pnl2, i, -5, -5);

        ang += 30;
        if(ang == 360) ang = 0;
    }
}


function drawDigi(bar, ang, num, br, idx, ofsx, ofsy) {

    var rad = ang * Math.PI / 180;
    var x = bar * Math.cos(rad) + OrgX + ofsx;
    var y = bar * Math.sin(rad) + OrgY + ofsy;
    br[idx].style.left = x;
    br[idx].style.top = y;

    if(num > -1) br[idx].innerText = num;
}

</SCRIPT>

</HEAD>

<BODY  bgcolor="indigo" scroll=no>

    <SCRIPT LANGUAGE="JScript">

        var str = ""
        for(i=1;i<=pnlcnt;i++) {
            str += "<SPAN id=pnl2>" + i + "</SPAN>";
            str += "<SPAN id=pnl1>" + i + "</SPAN>";
        }

        for(i=1;i<=lbrcnt;i++) {
            str += "<SPAN id=lbr></SPAN>";
        }

        for(i=1;i<=sbrcnt;i++) {
            str += "<SPAN id=sbr></SPAN>";
        }

        document.write(str);

    </SCRIPT>

</BODY>