Page 1 of 1

Vector Drawing: Put an Arrow endpoint on your arc

Posted: Thu Sep 22, 2016 1:43 am
by netmaestro
I have a project that draws a circular progress bar using gdiplus. It dawned on me today that with the Vector Drawing library at our disposal, we no longer have to dip into the gdiplus bare metal anymore, we can do most things with native PB. However, to my acute disappointment, there is no flag for StrokePath() that will draw your stroke ending in an arrow pointer. Gdiplus itself has this nice command: GdipSetPenEndCap(*pen1, #LineCapArrowAnchor) but there is no corresponding capability in StrokePath(). So - I worked out how to do it manually (with actually not that much trouble). Here is the result:

Code: Select all

; Vector Drawing Demo: Draw an endpoint on a partial circle 

Enumeration image
  #Base
  #Final
EndEnumeration

Declare RenderProgressBar(void)

If CreateImage(#Base, 200, 200, 24, RGB(255,255,255))
  If StartDrawing(ImageOutput(#Base))
    For j=0 To 180 Step 20
      For i=0 To 180 Step 20
        Box(i,j,10,10,RGB(220,220,220))
        Box(i+10,j+10,10,10,RGB(220,220,220))
      Next
    Next
    Box(40,40,120,120,RGB(0,0,0))
    Box(41,41,118,118,RGB(220,220,220))
    Box(50,50,100,100,RGB(0,0,0))
    Box(51,51,98,98,RGB(255,255,255))
    StopDrawing()
  EndIf
EndIf

If OpenWindow(0, 0, 0, 200, 200, "VectorDrawing", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  CopyImage(#Base, #Final)
  ImageGadget(0, 0, 0, 200, 200, ImageID(#Final))
  tid = CreateThread(@RenderProgressBar(), 0)
  Repeat
    Event = WaitWindowEvent()
  Until Event = #PB_Event_CloseWindow
EndIf

Procedure RenderProgressBar(void)
  Delay(500)
  progress.d = -90.0 ; To start at the top
  While progress < 270.0
    CopyImage(#Base, #Final)
    If StartVectorDrawing(ImageVectorOutput(#Final))
      VectorSourceLinearGradient(0,0,200,200)
      VectorSourceGradientColor(RGBA(255, 0, 0, 255), 0.0)
      VectorSourceGradientColor(RGBA(255, 255, 0, 255), 1.0)
      
      ; partial circle
      AddPathCircle(100, 100, 90, -90, progress)
      
      ; Save needed values before clearing the path
      angle.d = PathPointAngle(PathLength())+90    
      x.d=PathCursorX()
      y.d=PathCursorY()
      StrokePath(10)
      
      ; Draw endcap arrow using saved coordinates
      If progress < 269 
        RotateCoordinates(x,y,angle)
        MovePathCursor(x-10,y)
        AddPathLine(x,y-20)
        AddPathLine(x+10,y)
        ClosePath()
        FillPath()
      EndIf
      StopVectorDrawing()
      progress + 0.1
    EndIf
    
    SetGadgetState(0, ImageID(#Final))
    Delay(1)
    
  Wend
  Delay(200)
  SetGadgetState(0, ImageID(#Base))
EndProcedure

Re: Vector Drawing: Put an Arrow endpoint on your arc

Posted: Thu Sep 22, 2016 3:10 am
by davido
@netmaestro,

Very nice and instructive demo.
Thank you for sharing. :D

Tested on MacBook Pro. PureBasic 5.50

Re: Vector Drawing: Put an Arrow endpoint on your arc

Posted: Thu Sep 22, 2016 9:18 am
by Kwai chang caine
Nice one, and works very well here W7 X86 v5.50
Another way for waiting in circle like JAVA progressbar
I don't know if it's possible, but perhaps it's possible to show this progressbar with a transparent window for see just the arrow

Thanks for sahring 8)

Re: Vector Drawing: Put an Arrow endpoint on your arc

Posted: Thu Sep 22, 2016 9:34 am
by netmaestro
For drawing just the progress bar to the desktop, draw it to a 32bit transparent image and render it to the screen with UpdateLayeredWindow.

Re: Vector Drawing: Put an Arrow endpoint on your arc

Posted: Fri Sep 23, 2016 12:32 pm
by Kwai chang caine
Cool !!! That's a good news :D
Can be very usefull ...
Thanks again for your present and also for your answer 8)

Re: Vector Drawing: Put an Arrow endpoint on your arc

Posted: Sat Sep 24, 2016 12:44 am
by netmaestro
Example for Windows of drawing progress directly to the desktop:

Code: Select all

#TIME_KILL_SYNCHRONOUS = $0100
#LineWidth = 10

Enumeration image
  #Base
  #Output
EndEnumeration

LoadFont(0, "Verdana", 8, #PB_Font_Bold)
Global displaydot$ = #Empty$

Procedure TimerPulse(uTimerID, uMsg, dwUser, dw1, dw2)
  Shared progress.d, x.d, y.d
  CopyImage(#base, #Output)
  vt$ = "Working"+displaydot$
  If StartVectorDrawing(ImageVectorOutput(#Output))
    VectorFont(FontID(0))
    VectorSourceColor(RGBA(128, 0, 255, 255))
    MovePathCursor(62,90)
    DrawVectorText(vt$)
    FillPath()
    VectorSourceLinearGradient(0,0,200,200)
    VectorSourceGradientColor(RGBA(255, 0, 0, 255), 0.0)
    VectorSourceGradientColor(RGBA(255, 255, 0, 255), 1.0)
    
    ; partial circle
    AddPathCircle(100, 100, 60, -90, progress)
    
    ; Save needed values before clearing the path
    angle.d = PathPointAngle(PathLength())+90    
    x.d=PathCursorX()
    y.d=PathCursorY()
    
    StrokePath(#LineWidth)
    
    ; Draw endcap arrow using saved coordinates
    If progress < 269 
      RotateCoordinates(x,y,angle)
      MovePathCursor(x-#LineWidth,y)
      AddPathLine(x,y-#LineWidth*2)
      AddPathLine(x+#LineWidth,y)
      ClosePath()
      FillPath()
    EndIf
    StopVectorDrawing()
    progress + 0.1
    
  EndIf
  
  hDC = StartDrawing(ImageOutput(#Output))
  If hDC
    With sz.SIZE
      \cx = ImageWidth(0)
      \cy = ImageHeight(0)
    EndWith
    ContextOffset.POINT
    With BlendMode.BLENDFUNCTION
      \SourceConstantAlpha = 255
      \AlphaFormat = 1
    EndWith
    UpdateLayeredWindow_(WindowID(0), 0, 0, sz, hDC, ContextOffset, 0, BlendMode, 2)
    StopDrawing()
  EndIf
  
EndProcedure

Procedure dots(void)
  Repeat
    displaydot$ = ""
    Delay(400)
    displaydot$ = "."
    Delay(100)
    displaydot$= ".."
    Delay(100)
    displaydot$= "..."
    Delay(100)
    displaydot$="...."
    Delay(400)
  ForEver
EndProcedure

OpenWindow(0,0,0,200,200,"",#PB_Window_BorderLess|#PB_Window_ScreenCentered|#PB_Window_Invisible)
SetWindowLongPtr_(WindowID(0), #GWL_EXSTYLE, #WS_EX_LAYERED|#WS_EX_TOOLWINDOW)
StickyWindow(0, 1)
CreateImage(#Base, 200,200, 32, RGBA(255,255,255,255))
progress.d = -90.0 ; To start at the top
timer = timeSetEvent_(2, 1, @TimerPulse(), 0, #TIME_PERIODIC|#TIME_KILL_SYNCHRONOUS)
HideWindow(0,0)
tid = CreateThread(@dots(),0)
Repeat : WaitWindowEvent(1) : Until progress > 270.0
timeKillEvent_(timer)
KillThread(tid)
WaitThread(tid)
End

Re: Vector Drawing: Put an Arrow endpoint on your arc

Posted: Mon Sep 26, 2016 10:55 am
by Kwai chang caine
MASTER....you are a mother for me !! 8)

Image

Really splendid !!!
Again more nice than the JAVA logo

One million of thanks for sharing this jewel 8)