Danke für den Hinweis, habs jetzt überarbeitet.#NULL hat geschrieben:Bei der Demo hinter dem Showcase Link stimmt irgendwas nicht, die Minutenstriche und der Minutenzeiger stimmen nicht ganz überein.

Danke für den Hinweis, habs jetzt überarbeitet.#NULL hat geschrieben:Bei der Demo hinter dem Showcase Link stimmt irgendwas nicht, die Minutenstriche und der Minutenzeiger stimmen nicht ganz überein.
Code: Alles auswählen
DeclareModule vectorDrawingClock
EnableExplicit
Macro sConfig_private
_last_s.f
_last_ms.f
EndMacro
Structure sConfig ; for default values see getDefaultConfig()
canvas.i ; -1 if not set. otherwise the PureBasic canvas object #number used as a drawing output.
image.i ; -1 if not set. otherwise the PureBasic image object #number used as a drawing output.
colorBackground.i ; Background color of the entire output area.
colorClockFill.i ; Background color of the clock area.
colorClockRim.i ; Stroke color of the clock rim.
colorTick.i ; Stroke color of minute ticks.
colorTick5th.i ; Stroke color of 5th minute ticks.
colorTick15th.i ; Stroke color of 15th minute ticks.
colorHourHand.i ; Stroke color of the hour hand.
colorMinuteHand.i ; Stroke color of the minute hand.
colorSecondHand.i ; Stroke color of the second hand.
radiusClockRim.f ; 0.0 .. 1.0, percentage, relative to space available.
radiusHourHand.f ; 0.0 .. 1.0, percentage, relative to clock rim radius.
radiusMinuteHand.f ; 0.0 .. 1.0, percentage, relative to clock rim radius.
radiusSecondHand.f ; 0.0 .. 1.0, percentage, relative to clock rim radius.
radiusTickFrom.f ; 0.0 .. 1.0, percentage, relative to clock rim radius.
radiusTickTo.f ; 0.0 .. 1.0, percentage, relative to clock rim radius.
radiusTick5thFrom.f ; 0.0 .. 1.0, percentage, relative to clock rim radius.
radiusTick5thTo.f ; 0.0 .. 1.0, percentage, relative to clock rim radius.
radiusTick15thFrom.f ; 0.0 .. 1.0, percentage, relative to clock rim radius.
radiusTick15thTo.f ; 0.0 .. 1.0, percentage, relative to clock rim radius.
lineWidthClockRim.f ; Stroke strength.
lineWidthHourHand.f ; Stroke strength.
lineWidthMinuteHand.f ; Stroke strength.
lineWidthSecondHand.f ; Stroke strength.
lineWidthTick.f ; Stroke strength.
lineWidthTick5th.f ; Stroke strength.
lineWidthTick15th.f ; Stroke strength.
roundCaps.i ; Bool, #False results in square line ends, #True results in round line ends.
smoothHours.i ; Bool, #False results in hours hand pointing at full hours only, #True shows progress between hours.
smoothMinutes.i ; Bool, #False results in minutes hand pointing at full minutes only, #True shows progress between minutes.
smoothSeconds.i ; Bool, #False results in seconds hand pointing at full seconds only, #True shows progress between hours. (continuous second hand movement)
smootherSeconds.i ; Bool, similar to smoothSeconds but with an ease-in-out interpolation.
sConfig_private ; (private fields hidden from IDE)
EndStructure
Declare update(canvas, date = -1)
Declare.i getDefaultConfig()
EndDeclareModule
Module vectorDrawingClock
EnableExplicit
Define msSecondSwitch.q
Define thread, mutex
Procedure getSecondSwitch(*param.Quad)
Shared mutex
Protected second, second2, milliseconds.q
second = Second(Date())
Repeat
second2 = Second(Date())
milliseconds = ElapsedMilliseconds()
Until second2 <> second
*param\q = milliseconds
UnlockMutex(mutex)
EndProcedure
CompilerIf Not #PB_Compiler_Thread
CompilerError "Please enable the threadsafe compiler option."
CompilerEndIf
ElapsedMilliseconds()
If (Not mutex) And (Not thread)
mutex = CreateMutex()
; our mutex will be locked until the thread is done.
LockMutex(mutex)
; get a milliseconds value at which a second switch occurred.
thread = CreateThread( @ getSecondSwitch(), @ msSecondSwitch)
EndIf
Procedure.i getDefaultConfig()
Protected *config.sConfig
*config = AllocateStructure(sConfig)
*config\canvas = -1
*config\image = -1
*config\colorBackground = $ffffffff
*config\colorClockFill = $ffdddddd
*config\colorClockRim = $ff444444
*config\colorTick = $ff444444
*config\colorTick5th = $ff333333
*config\colorTick15th = $ff222222
*config\colorHourHand = $ff444444
*config\colorMinuteHand = $ff444444
*config\colorSecondHand = $ff444444
*config\radiusClockRim = 0.9
*config\radiusHourHand = 0.45
*config\radiusMinuteHand = 0.8
*config\radiusSecondHand = 0.9
*config\radiusSecondHand = 0.85
*config\radiusTickFrom = 0.95
*config\radiusTickTo = 0.96
*config\radiusTick5thFrom = 0.92
*config\radiusTick5thTo = 0.96
*config\radiusTick15thFrom = 0.90
*config\radiusTick15thTo = 0.96
*config\lineWidthClockRim = 6.0
*config\lineWidthHourHand = 8.0
*config\lineWidthMinuteHand = 5.0
*config\lineWidthSecondHand = 3.0
*config\lineWidthTick = 3.0
*config\lineWidthTick5th = 4.0
*config\lineWidthTick15th = 5.0
*config\roundCaps = #True
*config\smoothHours = #False
*config\smoothMinutes = #False
*config\smoothSeconds = #False
*config\smootherSeconds = #False
ProcedureReturn *config
EndProcedure
Procedure.f easeInOut(x.f)
If x < 0.5
; [0 .. 0.5] --> [0 .. 1]
x * 2.0
ProcedureReturn 0.5 * ((x) * Pow(x, 2))
Else
; [0.5 .. 1.0] --> [0 .. 1]
x = (x - 0.5) * 2.0
; 1-x = flip left/right
; 1 - .. = flip top/bottom
ProcedureReturn 0.5 + 0.5 * (1 - (1-x) * Pow(1-x, 2))
EndIf
EndProcedure
Procedure update(*config.sConfig, date = -1)
Protected h, m, s, ms, msFact.f
Protected width, height, smallerSize, centerX, centerY, caps
Protected radiusMax.f, radiusRim.f, radius.f, angle.f
Protected c, context
Shared mutex, msSecondSwitch
If date = -1
date = Date()
EndIf
h = Hour(date)
m = Minute(date)
s = Second(date)
; loop 2 times, to check for canvas or image (or both)
For c=0 To 1
context = 0
If (c = 0) And (*config\canvas >= 0)
context = StartVectorDrawing(CanvasVectorOutput(*config\canvas))
ElseIf (c = 1) And (*config\image >= 0)
context = StartVectorDrawing(ImageVectorOutput(*config\image))
EndIf
If context
width = VectorOutputWidth()
height = VectorOutputHeight()
smallerSize = width
If height < smallerSize
smallerSize = height
EndIf
radiusMax = smallerSize / 2
centerX = width / 2
centerY = height / 2
caps = #PB_Path_SquareEnd
If *config\roundCaps
caps = #PB_Path_RoundEnd
EndIf
; background
VectorSourceColor(*config\colorBackground)
FillVectorOutput()
; clock rim/fill
radiusRim = *config\radiusClockRim * radiusMax
AddPathCircle(centerX, centerY, radiusRim, 0, 360)
VectorSourceColor(*config\colorClockFill)
FillPath(#PB_Path_Preserve)
VectorSourceColor(*config\colorClockRim)
StrokePath(*config\lineWidthClockRim)
; ticks
Protected t, rFrom, rTo, color, lineWidth
For t=0 To 59
angle = -Radian(90) + Radian(t * (360/60))
If t%15 = 0
; 15 minutes ticks
rFrom = radiusRim * *config\radiusTick15thFrom
rTo = radiusRim * *config\radiusTick15thTo
color = *config\colorTick15th
lineWidth = *config\lineWidthTick15th
ElseIf t%5 = 0
; 5 minute ticks
rFrom = radiusRim * *config\radiusTick5thFrom
rTo = radiusRim * *config\radiusTick5thTo
color = *config\colorTick5th
lineWidth = *config\lineWidthTick5th
Else
; normal minute ticks
rFrom = radiusRim * *config\radiusTickFrom
rTo = radiusRim * *config\radiusTickTo
color = *config\colorTick
lineWidth = *config\lineWidthTick
EndIf
MovePathCursor(centerX + rFrom * Cos(angle), centerY + rFrom * Sin(angle))
AddPathLine(centerX + rTo * Cos(angle), centerY + rTo * Sin(angle))
VectorSourceColor(color)
StrokePath(lineWidth, caps)
Next
; hour hand
angle = -Radian(90) + Radian(h * (360/ 12))
If *config\smoothHours
angle + Radian(m * (360 / 60) / 12)
EndIf
radius = *config\radiusHourHand * radiusRim
MovePathCursor(centerX, centerY)
AddPathLine(radius * Cos(angle), radius * Sin(angle), #PB_Path_Relative)
VectorSourceColor(*config\colorHourHand)
StrokePath(*config\lineWidthHourHand, caps)
; minute hand
angle = -Radian(90) + Radian(m * (360 / 60))
If *config\smoothMinutes
angle + Radian(s * (360 / 60) / 60)
EndIf
radius = *config\radiusMinuteHand * radiusRim
MovePathCursor(centerX, centerY)
AddPathLine(radius * Cos(angle), radius * Sin(angle), #PB_Path_Relative)
VectorSourceColor(*config\colorMinuteHand)
StrokePath(*config\lineWidthMinuteHand, caps)
; second hand
angle = -Radian(90) + Radian(s * (360 / 60))
; smooth seconds only if the thread is done
If (*config\smoothSeconds Or *config\smootherSeconds) And TryLockMutex(mutex)
; just wanted to know if the thread is done, we dont really need the mutex for protection.
UnlockMutex(mutex)
; get how many milliseconds we are into the current second by removing any full seconds since our second switch.
ms = (ElapsedMilliseconds() - msSecondSwitch) % 1000
; our ms value (from ElapsedMilliseconds()) and our s value (from Date()) can be slightly
; off and not be 100% aligned to each other, so it could happen for example that the ms
; resets from 999 to 0 while the second is still the same and will only increases in a following
; frame, causing the second hand to jump back for a short moment. We fix that by using an older
; ms value if the second did not change yet.
If (ms < *config\_last_ms) And (s = *config\_last_s)
ms = *config\_last_ms
EndIf
; similarly, if the ms still increased while the second already proceeded, we reset the ms manually.
If (ms > *config\_last_ms) And (s > *config\_last_s)
ms = 0
EndIf
; smoother seconds movement
If *config\smootherSeconds
msFact = ms / 1000.0 ; normalize to 0..1
msFact = easeInOut(msFact) ; ease
ms = 1000.0 * msFact ; convert back to ms
EndIf
; add smooth[er] part to seconds angle
angle + Radian(ms * (360 / 60) / 1000)
*config\_last_s = s
*config\_last_ms = ms
EndIf
radius = *config\radiusSecondHand * radiusRim
MovePathCursor(centerX, centerY)
AddPathLine(radius * Cos(angle), radius * Sin(angle), #PB_Path_Relative)
VectorSourceColor(*config\colorSecondHand)
StrokePath(*config\lineWidthSecondHand, caps)
StopVectorDrawing()
EndIf
Next
EndProcedure
EndModule
CompilerIf #PB_Compiler_IsMainFile
EnableExplicit
Define ww, wh, event, quit
Define win1, canvas1
Define win2, image2, img2
Define win3, canvas3
Define *clockConfig1.vectorDrawingClock::sConfig
Define *clockConfig2.vectorDrawingClock::sConfig
Define *clockConfig3.vectorDrawingClock::sConfig
; ----------------------------------------------------------------------
; default clock on a canvas
ww=600
wh=400
win1 = OpenWindow(#PB_Any, 100, 100, ww, wh, "1, default", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_SizeGadget)
canvas1 = CanvasGadget(#PB_Any, 0, 0, ww, wh)
AddKeyboardShortcut(win1, #PB_Shortcut_Escape, 10)
*clockConfig1 = vectorDrawingClock::getDefaultConfig()
*clockConfig1\canvas = canvas1
Procedure updateClock1()
Shared *clockConfig1
vectorDrawingClock::update(*clockConfig1, Date())
EndProcedure
Procedure resize1()
Shared win1, canvas1
ResizeGadget(canvas1, 0, 0, WindowWidth(win1), WindowHeight(win1))
updateClock1()
EndProcedure
BindEvent(#PB_Event_SizeWindow, @ resize1(), win1)
AddWindowTimer(win1, 1, 100)
BindEvent(#PB_Event_Timer, @ updateClock1(), win1, 1)
; ----------------------------------------------------------------------
; randomized clock on an image
ww=400
wh=600
win2 = OpenWindow(#PB_Any, 720, 100, ww, wh, "2, randomized", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_SizeGadget)
img2 = CreateImage(#PB_Any, ww, wh)
image2 = ImageGadget(#PB_Any, 0, 0, ww, wh, ImageID(img2))
AddKeyboardShortcut(win2, #PB_Shortcut_Escape, 10)
*clockConfig2 = vectorDrawingClock::getDefaultConfig()
*clockConfig2\image = img2
Procedure updateClock2()
Shared image2, img2, *clockConfig2
vectorDrawingClock::update(*clockConfig2, Date())
SetGadgetState(image2, ImageID(img2))
EndProcedure
Procedure resize2()
Shared win2, image2
ResizeGadget(image2, 0, 0, WindowWidth(win2), WindowHeight(win2))
updateClock2()
EndProcedure
Procedure randomizeConfig2()
Shared *clockConfig2
*clockConfig2\colorBackground = RGBA(Random(255), Random(255), Random(255), Random(255))
*clockConfig2\colorClockFill = RGBA(Random(255), Random(255), Random(255), Random(255))
*clockConfig2\colorClockRim = RGBA(Random(255), Random(255), Random(255), Random(255))
*clockConfig2\colorTick = RGBA(Random(255), Random(255), Random(255), Random(255))
*clockConfig2\colorTick5th = RGBA(Random(255), Random(255), Random(255), Random(255))
*clockConfig2\colorTick15th = RGBA(Random(255), Random(255), Random(255), Random(255))
*clockConfig2\colorHourHand = RGBA(Random(255), Random(255), Random(255), Random(255))
*clockConfig2\colorMinuteHand = RGBA(Random(255), Random(255), Random(255), Random(255))
*clockConfig2\colorSecondHand = RGBA(Random(255), Random(255), Random(255), Random(255))
*clockConfig2\radiusClockRim = 0.4 + 0.01 * Random(60)
*clockConfig2\radiusHourHand = 0.4 + 0.01 * Random(30)
*clockConfig2\radiusMinuteHand = 0.4 + 0.01 * Random(30)
*clockConfig2\radiusSecondHand = 0.4 + 0.01 * Random(30)
*clockConfig2\radiusTickFrom = 0.8 + 0.01 * Random(10)
*clockConfig2\radiusTickTo = 0.8 + 0.01 * Random(10)
*clockConfig2\radiusTick5thFrom = 0.8 + 0.01 * Random(10)
*clockConfig2\radiusTick5thTo = 0.8 + 0.01 * Random(10)
*clockConfig2\radiusTick15thFrom = 0.8 + 0.01 * Random(10)
*clockConfig2\radiusTick15thTo = 0.8 + 0.01 * Random(10)
*clockConfig2\lineWidthClockRim = 1.0 + Random(30)
*clockConfig2\lineWidthHourHand = 1.0 + Random(30)
*clockConfig2\lineWidthMinuteHand = 1.0 + Random(30)
*clockConfig2\lineWidthSecondHand = 1.0 + Random(30)
*clockConfig2\lineWidthTick = 1.0 + Random(12)
*clockConfig2\lineWidthTick5th = 1.0 + Random(12)
*clockConfig2\lineWidthTick15th = 1.0 + Random(12)
*clockConfig2\roundCaps = Bool(Random(1))
EndProcedure
BindEvent(#PB_Event_SizeWindow, @ resize2(), win2)
AddWindowTimer(win2, 21, 100)
BindEvent(#PB_Event_Timer, @ updateClock2(), win2, 21)
AddWindowTimer(win2, 22, 1700)
BindEvent(#PB_Event_Timer, @ randomizeConfig2(), win2, 22)
; ----------------------------------------------------------------------
; smooth hand movement.
; see the select below for continuous or natural second hand movement.
ww=300
wh=200
win3 = OpenWindow(#PB_Any, 100, 550, ww, wh, "3, smooth movement", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_SizeGadget)
canvas3 = CanvasGadget(#PB_Any, 0, 0, ww, wh)
AddKeyboardShortcut(win3, #PB_Shortcut_Escape, 10)
*clockConfig3 = vectorDrawingClock::getDefaultConfig()
*clockConfig3\canvas = canvas3
*clockConfig3\smoothHours = #True
*clockConfig3\smoothMinutes = #True
Select 2
Case 1 : *clockConfig3\smoothSeconds = #True
Case 2 : *clockConfig3\smootherSeconds = #True
EndSelect
Procedure updateClock3()
Shared *clockConfig3
vectorDrawingClock::update(*clockConfig3, Date())
EndProcedure
Procedure resize3()
Shared win3, canvas3
ResizeGadget(canvas3, 0, 0, WindowWidth(win3), WindowHeight(win3))
updateClock3()
EndProcedure
BindEvent(#PB_Event_SizeWindow, @ resize3(), win3)
AddWindowTimer(win3, 3, 100)
BindEvent(#PB_Event_Timer, @ updateClock3(), win3, 3)
; ----------------------------------------------------------------------
Repeat
event = WaitWindowEvent(10)
Select event
Case #PB_Event_CloseWindow
quit = #True
Case #PB_Event_Menu
Select EventMenu()
Case 10
quit = #True
EndSelect
EndSelect
Until quit
CompilerEndIf
; configs are allocated and should be freed in case the program does not end
FreeStructure(*clockConfig1)
*clockConfig1 = 0
FreeStructure(*clockConfig2)
*clockConfig2 = 0
FreeStructure(*clockConfig3)
*clockConfig3 = 0