'=================================
'    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