DrawVector RotateCoordinates, Box drehen + Ecken berechnen

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
Benutzeravatar
dige
Beiträge: 1182
Registriert: 08.09.2004 08:53

Re: DrawVector RotateCoordinates, Box drehen + Ecken berechn

Beitrag von dige »

#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. :allright:
"Papa, mein Wecker funktioniert nicht! Der weckert immer zu früh."
Benutzeravatar
#NULL
Beiträge: 2235
Registriert: 20.04.2006 09:50

Re: DrawVector RotateCoordinates, Box drehen + Ecken berechn

Beitrag von #NULL »

Ich habe es nochmal erweitert. Die Zeiger zeigen jetzt auch die Zwischenschritte an sodass z.B. der Stundenzeiger nicht immer nur auf der vollen Stunde steht und der Sekundenzeiger bewegt sich optional natürlicher (Fenster 3, im Select anpassen). Das übergibt man alles in einer Config-Struktur (Fenster 2 macht das zufällig mit Farben und Stärken/Größen).

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


my pb stuff..
Bild..jedenfalls war das mal so.
Benutzeravatar
DrShrek
Beiträge: 1970
Registriert: 08.09.2004 00:59

Re: DrawVector RotateCoordinates, Box drehen + Ecken berechn

Beitrag von DrShrek »

Hat dieses "Uhr-Thema" etwas mit dem Titel und dem ersten Beitrag was gemeinsames?
Vielleicht mag ein Forum-Admin das auftrennen?
Siehste! Geht doch....?!
PB*, *4PB, PetriDish, Movie2Image, PictureManager, TrainYourBrain, ...
Antworten