summaryrefslogtreecommitdiff
path: root/docs/chapter31/examples/Clock.smp
blob: 900126e15046c766d546fd08eefda1b6495c45b6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
// Local clocks; click the button or press Space to switch. Q/Escape exits.
// Any argument runs ten frames, exercising both faces for smoke tests.
u32 GREEN=0x62ff70, DIM=0x123d20, BACKGROUND=0x020b05;
val main = { u8[~][] args ->
    val screen=new sys.draw.Canvas(720,540);
    if( sys.gfx.open("Simple digital and analog clocks",screen.width,screen.height)!=sys.gfx.OK ) return 1;
    val button=new sys.draw.Button(220,454,280,52,"ANALOG CLOCK");
    val timers=new sys.time.Timers();
    i64 frame=timers.every(33);
    if( frame<0 ) { sys.gfx.close(); return 1; }
    bool analog=false, held=false;
    int frames=0, status=sys.gfx.OK;
    while( status==sys.gfx.OK ) {
        bool space=sys.gfx.key(' ');
        bool down=sys.gfx.button(sys.gfx.LEFT);
        bool click=button.update(sys.gfx.mouseX(),sys.gfx.mouseY(),down);
        if( click || (space && !held) ) analog=!analog;
        held=space;
        button.label=analog ? "DIGITAL CLOCK" : "ANALOG CLOCK";
        if( sys.gfx.key('Q') ) break;
        if( timers.poll(sys.time.monotonic())==frame ) {
            _draw(screen,sys.time.now(),analog);
            button.draw(screen,3,GREEN,BACKGROUND,DIM);
            status=sys.gfx.present(screen.pixels);
            if( args#>1 ) {
                frames++;
                if( frames==5 ) analog=true;
                if( frames==10 ) break;
            }
        }
        if( status==sys.gfx.OK ) status=sys.gfx.wait(sys.time.remaining(timers.nextDeadline(),sys.time.monotonic()));
    }
    sys.gfx.close();
    return status==sys.gfx.ERROR ? 1 : 0;
};
val _draw = { !sys.draw.Canvas screen, i64 wall, i64 analog ->
    val d=sys.time.local(wall);
    screen.clear(BACKGROUND);
    screen.text(266,24,"SIMPLE CLOCK",4,GREEN);
    if( analog ) _analog_noInline(screen,d.hour,d.minute,d.second,d.millisecond);
    else _digital_noInline(screen,d.hour,d.minute,d.second);
    screen.text(298,412,"%(sys.time.formatLocal(wall,sys.time.DATE))",4,GREEN);
    screen.text(249,520,"SPACE SWITCH   Q OR ESC QUIT",2,GREEN);
};
// a..g bits: top, upper-right, lower-right, bottom, lower-left, upper-left,
// middle. Faint unlit bars retain the old LED figure-eight silhouette.
val _digit_noInline = { !sys.draw.Canvas c, int x, int y, i64 digit ->
    int bits=digit==0 ? 0x3f : digit==1 ? 0x06 : digit==2 ? 0x5b :
        digit==3 ? 0x4f : digit==4 ? 0x66 : digit==5 ? 0x6d :
        digit==6 ? 0x7d : digit==7 ? 0x07 : digit==8 ? 0x7f : digit==9 ? 0x6f : 0;
    for( int i=0; i<7; i++ ) {
        u32 color=(bits&(1<<i))!=0 ? GREEN : DIM;
        if( i==0 || i==3 || i==6 ) {
            int dy=i==0 ? 0 : i==3 ? 144 : 72;
            c.line(x+14,y+dy,x+58,y+dy,10,color);
        } else {
            bool right=i==1 || i==2;
            int dx=right ? 68 : 4, dy=i==1 || i==5 ? 12 : 84;
            c.line(x+dx,y+dy,x+dx,y+dy+48,10,color);
        }
    }
};
val _digital_noInline = { !sys.draw.Canvas c, int hour, int minute, int second ->
    _digit_noInline(c,68,166,hour/10); _digit_noInline(c,154,166,hour-hour/10*10);
    _digit_noInline(c,284,166,minute/10); _digit_noInline(c,370,166,minute-minute/10*10);
    _digit_noInline(c,500,166,second/10); _digit_noInline(c,586,166,second-second/10*10);
    for( int i=0; i<2; i++ ) {
        c.fillCircle(254+i*216,214,6,GREEN);
        c.fillCircle(254+i*216,262,6,GREEN);
    }
    c.text(272,352,"LOCAL TIME  24H",3,GREEN);
};
// Rational sine approximation, accurate enough for pixel clock geometry.
// Arguments are nonnegative turns; no additional native math dependency.
val _sin_noInline = { f64 turn ->
    i64 whole=turn; turn-=whole;
    bool negative=turn>0.5;
    f64 x=(negative ? turn-0.5 : turn)*6.283185307179586;
    f64 product=x*(3.141592653589793-x);
    f64 numerator=negative ? -16.0 : 16.0;
    return numerator*product/(49.34802200544679-4*product);
};
val _hand_noInline = { !sys.draw.Canvas c, f64 turn, int length, int thickness, u32 color ->
    int x=360+_sin_noInline(turn)*length, y=232-_sin_noInline(turn+0.25)*length;
    c.line(360,232,x,y,thickness,color);
};
val _analog_noInline = { !sys.draw.Canvas c, int hour, int minute, int second, int millisecond ->
    c.circle(360,232,154,3,GREEN);
    for( int i=0; i<60; i++ ) {
        f64 turn=i/60.0;
        bool major=i-i/5*5==0;
        int inner=major ? 133 : 143;
        int x0=360+_sin_noInline(turn)*inner, y0=232-_sin_noInline(turn+0.25)*inner;
        int x1=360+_sin_noInline(turn)*149, y1=232-_sin_noInline(turn+0.25)*149;
        c.line(x0,y0,x1,y1,major ? 3 : 1,GREEN);
    }
    c.text(349,100,"12",2,GREEN); c.text(484,227,"3",2,GREEN);
    c.text(357,354,"6",2,GREEN); c.text(230,227,"9",2,GREEN);
    f64 seconds=second+millisecond/1000.0;
    f64 minutes=minute+seconds/60.0;
    _hand_noInline(c,(hour-hour/12*12+minutes/60.0)/12.0,78,8,GREEN);
    _hand_noInline(c,minutes/60.0,112,5,GREEN);
    _hand_noInline(c,seconds/60.0,128,2,0xc3ffcb);
    c.fillCircle(360,232,6,GREEN);
};