'=================================
' ANALOG CLOCK
'
' Created: 1985
' Ported in small basic: 2018
' Current version: 23 January 2023
'
' This program is transcribed as faithfully
' as possible from an original written for
' MS-DOS 3.0 BASICA at some time in 1985
' (perhaps at the beginning of 1986) by my
' colleague Luciano Ghinelli and me (Cesare
' Brizio).
' Minor improvements were added in January
' 2023 (variables renamed, spurious pixels
' overwriting -the latter, a problem not observable
' in the old DOS program), but the core algorithm
' wasn't altered.
' As specified in the start-up text message,
' Luciano and me chose deliberately a more intricate
' alternative than the bare, integral regeneration of
' the clock face at every program cycle: in fact,
' while the clock's hands are constantly regenerated
' at their current position (the only way to prevent
' their disappearance when overtaken by another
' hand), they are individually deleted from their
' previous position just as they move - and this
' required a modicum of intricacy.
' It operated in "SCREEN 9" mode (640x350)
' Text coordinates were as usual for the time
' the 24 lines x 80 columns system.
' In hindsight, Small Basic's (for that purpose.
' any "recent" Basic's!) syntax seems to me
' convolute, inefficient and counter-intuitive!
'=================================
TextWindow.WriteLine("It's crystal clear that one could regenerate the whole clock face")
TextWindow.WriteLine("at every program cycle, by recreating the whole clock face,")
TextWindow.WriteLine("redrawing each time all the three hands, at the price of some")
TextWindow.WriteLine("excessive flickering. Such a program would be much simpler and")
TextWindow.WriteLine("shorter than this. The over-engineered approach preferred here is:")
TextWindow.WriteLine(" - draw the clock face only once at program start;")
TextWindow.WriteLine(" - at every cycle, rewrite only the hands at their current position;")
TextWindow.WriteLine(" - chech whether any hand should be moved to a new position;")
TextWindow.WriteLine(" - overwrite just the hand at the old position;")
TextWindow.WriteLine(" - rewrite the hand at ist new position.")
TextWindow.WriteLine("Press enter to start")
TextWindow.Read()
TextWindow.WriteLine("Click the graphics window and press any key to exit")
' The original clock filled entirely the "Screen 9" (640x350) window, and
' touched the top and the bottom window edge.
' Here, to improve its look, the clock is down-shifted by offset_y and
' the screen is enlarged by 2 * offset_y
' OFFSET_Y SHOULD BE KEPT ABOVE ZERO, OTHERWISE
' THE OUTERMOST CIRCLE MAY NOT BE GENERATED
offset_y = 20 'vertical offset in pixel
h = 350 + (offset_y*2) 'height
w = 640 + (offset_y*2) 'width
GraphicsWindow.Height = h
GraphicsWindow.Width = w
GraphicsWindow.KeyDown = OnKeyDown
GraphicsWindow.BackgroundColor = "Black"
GraphicsWindow.Show()
'------------------------------------------------------------------------------
' CALCULATING THE COORDINATES AROUND THE CIRCLE
'------------------------------------------------------------------------------
R = 150
X = 320
Y = 9
PI = 3.141593
IND = 0
FOR I = -PI / 2 TO PI * 3 / 2 STEP PI / 30
GRAD_COS = Math.Floor(320 + (R * math.Cos(I)*0.97))
SEC_COS = Math.Floor(320 + (R * math.Cos(I)*0.94))
MIN_COS = Math.Floor(320 + (R * math.Cos(I)*0.83))
HOUR_COS = Math.Floor(320 + (R * math.Cos(I)*0.70))
HAND_STARTS_FROM_X = Math.Floor(320 + (R * math.Cos(I)*0.08))
GRAD_START_FROM_X = Math.Floor(320 + ((R + 10) * math.Cos(I)))
GRAD_SIN = Math.Floor(175 + (R * math.Sin(I)*0.97))
SEC_SIN = Math.Floor(175 + (R * math.Sin(I)*0.94))
MIN_SIN = Math.Floor(175 + (R * math.Sin(I)*0.83))
HOUR_SIN = Math.Floor(175 + (R * math.Sin(I)*0.70))
HAND_STARTS_FROM_Y = Math.Floor(175 + (R * math.Sin(I)*0.08))
GRAD_START_FROM_Y = Math.Floor(175 + ((R + 10) * math.Sin(I)))
' See around line 20 for an explanation of offset_y
END_GRAD_X[IND] = GRAD_COS
END_GRAD_Y[IND] = GRAD_SIN+offset_y
HOURX[IND] = HOUR_COS
HOURY[IND] = HOUR_SIN+offset_y
MINX[IND] = MIN_COS
MINY[IND] = MIN_SIN+offset_y
SECX[IND] = SEC_COS
SECY[IND] = SEC_SIN+offset_y
START_GRAD_X[IND] = GRAD_START_FROM_X
START_GRAD_Y[IND] = GRAD_START_FROM_Y+offset_y
FromX[IND] = HAND_STARTS_FROM_X
FromY[IND] = HAND_STARTS_FROM_Y+offset_y
IND= IND + 1
EndFor
'----------------------------------------
' DRAWING THE CLOCK FACE
'----------------------------------------
'============> Initially all white
GraphicsWindow.BrushColor = "White"
GraphicsWindow.FillEllipse(144,0+offset_y,2*R + 50,2*R +50)
'============> DOUBLE OUTERMOST BORDER
GraphicsWindow.PenColor = "DodgerBlue"
GraphicsWindow.DrawEllipse(144,0+offset_y,2*R + 50,2*R +50)
GraphicsWindow.PenColor = "DodgerBlue"
GraphicsWindow.DrawEllipse(143,0+offset_y-1,2*R + 51,2*R +51)
'============> Clock face under the hands
GraphicsWindow.BrushColor = "Gainsboro"
GraphicsWindow.FillEllipse(170,25+offset_y,2*R,2*R) 'OK
'============> Two inner circles and graduations
GraphicsWindow.PenColor = "SlateGray"
GraphicsWindow.DrawEllipse(165,20+offset_y,2*R + 10,2*R +10)'OK
GraphicsWindow.DrawEllipse(170,25+offset_y,2*R,2*R) 'OK
FOR OO = 0 TO 60
GraphicsWindow.DrawLine(START_GRAD_X[OO],START_GRAD_Y[OO],END_GRAD_X[OO],END_GRAD_Y[OO])
EndFor
'============> Numbers
GraphicsWindow.BrushColor = "Black"
GraphicsWindow.DrawText(312,1+offset_y,"12")
GraphicsWindow.DrawText(224,26+offset_y,"11")
GraphicsWindow.DrawText(168,85+offset_y,"10")
GraphicsWindow.DrawText(150,167+offset_y,"9")
GraphicsWindow.DrawText(172,249+offset_y,"8")
GraphicsWindow.DrawText(228,309+offset_y,"7")
GraphicsWindow.DrawText(315,334+offset_y,"6")
GraphicsWindow.DrawText(402,309+offset_y,"5")
GraphicsWindow.DrawText(460,249+offset_y,"4")
GraphicsWindow.DrawText(482,167+offset_y,"3")
GraphicsWindow.DrawText(459,85+offset_y,"2")
GraphicsWindow.DrawText(402,26+offset_y,"1")
' ============> Innermost circle -colored as the SECONDS hand
GraphicsWindow.PenColor = "Lime"
GraphicsWindow.DrawEllipse(310,165+offset_y,20,20)
GraphicsWindow.BrushColor = "Lime"
GraphicsWindow.FillEllipse(310,165+offset_y,20,20)
OLD_HX=0
OLD_HY=0
PREV_H_HAND_POINTS_AT=0
exiting = "False" 'User interaction - Has any key been pressed??
While exiting = "False"
'----------------------------------------------------------------
' CALCULATING SECONDS AND COORDINATES
'----------------------------------------------------------------
SEC = Clock.Second
SX = SECX[SEC]
SY = SECY[SEC]
MSEC = SEC - 1
IF MSEC < 0 THEN
MSEC = 59
EndIf
OLD_SX = SECX[MSEC]
OLD_SY = SECY[MSEC]
GraphicsWindow.PenWidth = 1
GraphicsWindow.PenColor = "Lime"
GraphicsWindow.DrawLine(320, 175+offset_y, SX , SY)
GraphicsWindow.BrushColor = "Lime"
GraphicsWindow.fillEllipse(310,165+offset_y,20,20)
GraphicsWindow.BrushColor = "White"
GraphicsWindow.DrawText(0,0,Clock.Time)
Text.GetCharacter(65)
IF PSEC <> SEC THEN
Sound.PlayClick()
PSEC = SEC
ELSE
GOTO SecondUnchanged
EndIf
GraphicsWindow.PenWidth = 4
GraphicsWindow.PenColor = "Gainsboro"
' ---------------> This may leave one or more pixels at the old position of the hand's tip
'GraphicsWindow.DrawLine(320, 175+offset_y, OLD_SX , OLD_SY)
' ---------------> By cleaning all the radius up to the inner graduation, I am sure to overwrite the whole hand
GraphicsWindow.DrawLine(320, 175+offset_y, END_GRAD_X[MSEC] , END_GRAD_Y[MSEC])
GraphicsWindow.BrushColor = "Black"
GraphicsWindow.FillRectangle(0,0,100,20)
SecondUnchanged:
'----------------------------------------------------------------
' CALCULATING MINUTES AND COORDINATES
'----------------------------------------------------------------
MIN = Clock.Minute
MX = MINX[MIN]
MY = MINY[MIN]
MFROM_X=FromX[MIN]
MFROM_Y=FromY[MIN]
GraphicsWindow.PenWidth = 2
GraphicsWindow.PenColor = "Blue"
GraphicsWindow.DrawLine(MFROM_X, MFROM_Y, MX , MY)
IF (PREMIN = MIN) THEN
GOTO MinuteUnchanged
EndIf
PREMIN = MIN
MMIN = MIN - 1
IF (MMIN < 0) THEN
MMIN = 59
EndIf
OLD_MX = MINX[MMIN]
OLD_MY = MINY[MMIN]
OLD_MFROM_X=FromX[MMIN]
OLD_MFROM_Y=FromY[MMIN]
GraphicsWindow.PenWidth = 6
GraphicsWindow.PenColor = "Gainsboro"
' ---------------> This may leave one or more pixels at the old position of the hand's tip
'GraphicsWindow.DrawLine(OLD_MFROM_X, OLD_MFROM_Y, OLD_MX , OLD_MY)
' ---------------> By cleaning all the radius up to the inner graduation, I am sure to overwrite the whole hand
GraphicsWindow.DrawLine(OLD_MFROM_X, OLD_MFROM_Y, END_GRAD_X[MMIN] , END_GRAD_Y[MMIN])
MinuteUnchanged:
'----------------------------------------------------------------
' CALCULATING HOURS AND COORDINATES
'----------------------------------------------------------------
HOUR = Clock.Hour
HMIN = Clock.Minute
IF (HOUR > 11) THEN
HOUR = HOUR - 12
EndIf
H_HAND_POINTS_AT = HOUR * 5 + Math.Floor(HMIN / 12)
'textwindow.writeline(H_HAND_POINTS_AT) '--DEBUG
HX = HOURX[H_HAND_POINTS_AT]
HY = HOURY[H_HAND_POINTS_AT]
HFROM_X=FromX[H_HAND_POINTS_AT]
HFROM_Y=FromY[H_HAND_POINTS_AT]
GraphicsWindow.PenWidth = 4
GraphicsWindow.PenColor = "FireBrick"
GraphicsWindow.DrawLine(HFROM_X, HFROM_Y, HX , HY)
IF (H_HAND_POINTS_AT = PREV_H_HAND_POINTS_AT) THEN
GOTO HourUnchanged
EndIf
IF (OLD_HX = 0 AND OLD_HY = 0) THEN
OLD_HX = HX
OLD_HY = HY
OLD_HFROM_X=HFROM_X
OLD_HFROM_Y=HFROM_Y
Goto HourUnchanged
EndIf
GraphicsWindow.PenWidth = 8
GraphicsWindow.PenColor = "Gainsboro"
' ---------------> This may leave one or more pixels at the old position of the hand's tip
'GraphicsWindow.DrawLine(OLD_HFROM_X, OLD_HFROM_Y, OLD_HX , OLD_HY)
' ---------------> By cleaning all the radius up to the inner graduation, I am sure to overwrite the whole hand
GraphicsWindow.DrawLine(OLD_HFROM_X, OLD_HFROM_Y, END_GRAD_X[PREV_H_HAND_POINTS_AT] , END_GRAD_Y[PREV_H_HAND_POINTS_AT])
OLD_HX = HX
OLD_HY = HY
OLD_HFROM_X=HFROM_X
OLD_HFROM_Y=HFROM_Y
PREV_H_HAND_POINTS_AT = H_HAND_POINTS_AT
HourUnchanged:
endwhile
Program.End()
'-----------------------------------------------------------------------------------------------------------
'--- Take note of the fact that a key has been pressed.
'-----------------------------------------------------------------------------------------------------------
Sub OnKeyDown
exiting = "True"
EndSub