ProgressCircle (for Linux/Windows & Mac.. I guess))
@superadmin:
What is the advantage of showing a gif animation in a webgadget ? (that's what you suggest at least)
Where's the flexibility of such a thing? "skinning"?
we have: a resizeable gadget with flexible amount of segments which can be colored as you like.... not consuming much memory ... with an respectable optical effect which could take every status at every time DEVELOPED BY at least 3 Community members (so far).... that it has it's glitches.. no question... but together we can get it polished....
if you think, you're way is more flexible and less consuming memory...... show us how do you want to do that! but..: how many pictures do you want to create and deliver to satisfy all possible needs? ... and there are much more questions I could ask...
anyway.....
as Demivec wrote: I'd invited EVERYBODY to enhance this gadget... if you want to, code a procedure which shows only images and could be called with a (additional) parameter....(enhance the structure if needed)
if you need help, don't hesitate to ask or show where your code fails or doesn't work....
What is the advantage of showing a gif animation in a webgadget ? (that's what you suggest at least)
Where's the flexibility of such a thing? "skinning"?
we have: a resizeable gadget with flexible amount of segments which can be colored as you like.... not consuming much memory ... with an respectable optical effect which could take every status at every time DEVELOPED BY at least 3 Community members (so far).... that it has it's glitches.. no question... but together we can get it polished....
if you think, you're way is more flexible and less consuming memory...... show us how do you want to do that! but..: how many pictures do you want to create and deliver to satisfy all possible needs? ... and there are much more questions I could ask...
anyway.....
as Demivec wrote: I'd invited EVERYBODY to enhance this gadget... if you want to, code a procedure which shows only images and could be called with a (additional) parameter....(enhance the structure if needed)
if you need help, don't hesitate to ask or show where your code fails or doesn't work....
Code update:
- - Improved aspects of circle drawing to make it more accurate
- made 3 changes to ProgressCircle structure:
---- Added Size variable to specify size of circle during init, same as it was before last update. Size should always be even.
---- Changed radius variable to a float, the user should not set this variable now but use Size instead.
---- Added style variable to specify if circle is continous (=1) or segmented.
- continous circles are implemented!set style=1. This enables more segments to fit on a circle of a given size, i.e. for Size 50 you can have 46 segments on a continous circle instead of 41 with a segmented circle. Continuous circles accomplish the coloring of their segments by varying the SegmentColor by small amounts (-1,+0,+1).
Code: Select all
;- ProgressCircle
; 2008 by walker
; modifications by ebs
; modifications by Demivec
; modifications by walker
;--------------------------------------------
;-free for everyone and any purposes
;-enhancements made by others
;-must be published and posted in the same PB-Forum ;-)
;-cross plattform ability must be achieved
;--------------------------------------------
EnableExplicit
; [Demivec] added Size parameter for init instead of radius, changed radius to a float,
; added style parameter to select a continous or segmented circle (1=continuos)
;Allows more than one progressCircle to be displayed.
Structure ProgressCircle
;for init: need to set Size,segmentCount,segmentColor,backgroundColor,gx,gy,gadgetNum,imageNum
; all values used for init, excluding segmentColor are considered constants after init is performed
;between updates: can change segmentColor,textcolor, either fontID or newFont/fontNum
Size.l ;size of circle (even #'s only);used during init ;12 is minimum; maximum... your screen ;-)
radius.f ;radius of circle ;(1/2 Size) used internally and not to be changed by the user
segmentCount.l ;#of segments in circle ;any value allowed, working values are usually less than Size (i.e. segments<Size)
;for max segments at various sizes these work (seg,Size) (8,12) (12,16) (16,20) (32,30) (1 to 44,50) (100,120)
segmentColor.l ;color of circle ,during init the entire circle is drawn in this color
backgroundcolor.l ;background color of circle
textcolor.l ;color of text in circle
gx.l ;coordinates to create ImageGadget at (used only during init)
gy.l
;If any of the next two values are set to #PB_any, an init call will set them with their new value,
;if they are not equal to #PB_Any their value will be used for the desired item.
gadgetNum.l ;if =#PB_Any during init, will be equal to gadget# on exit
imageNum.l ;if =#PB_Any during init, will be equal to image# on exit
;A user should either set fontID to a pre-initialized fontID or they should set newFont to the name of font.
;If newFont is used it will have the side-effect of freeing the previous fontID, so for a given
;ProgressCircle only one method should be used during it's existance.
fontID.l ;font handle for displayed text, if not zero it is treated as valid (or pre-initialized)
newFont.s ;name of font to use load during next update, fontID will equal the font handle and this
;will equal "" after LoadFont is attempted
fontNum.l ;if =#PB_Any during init, will be equal to font# on exit or zero, this value can be read
;to determine if a new font was succesfully loaded (it will be non-zero if successful)
style.l ;indicates if using a continuous (=1) or a segmented circle (<>1)
EndStructure
; [Demivec] utilized a structure to hold ProgressCircle data, unified seperate procedures and created
; a command format to specify actions (rather OOP like), allowed font to be specified and changed,
; made it possible to use either #pb_any or custom values for gadget#,image#,fontID.
Procedure ProgressCircle(*pCircle.ProgressCircle, cmd.l, state.l = 1, text.s = "")
Protected x.l,y.l,x2.l,y2.l,m.l,TW2.l,TH2.l,rangeStart.l ;added x2,y2 to help with continuous circle
Protected angle_rad.f ; [Demivec] changed to a float
;Call procedure ProgressCircle with the forms below to accomplish each given task:
; ProgressCircle(@Data.ProgressCircle,10) Setup Data of ProgressCircle with default values
; ProgressCircle(@Data.ProgressCircle,0) Init ProgressCircle
; ProgressCircle(@Data.ProgressCircle,1,state# [,Text]) Draw a specific state# [, display Text]
; ProgressCircle(@Data.ProgressCircle,1,-state# [,Text]) Draw a range of states from 1 to +state# [, display Text]
; ProgressCircle(@Data.ProgressCircle,-1) Free circle elements (image,font,gadget), convenient to
; use if all three items were created by ProgressCircle.
Select cmd ;commands in order of likely use
;the command #'s are more or less arbitrary, may be better to replace with constants in future
;of the form #PC_SetDefaults_cmd or maybe these forms #PCCmd_Init,#PCCmd_Update,#PCCmd_FreeElements
Case 1 ;{draw state#
;check if current font should be replaced with a new font
If *pCircle\newFont <> ""
If *pCircle\fontNum <> #PB_Any
FreeFont(*pCircle\fontNum) ;free font if a previous font was loaded
EndIf
*pCircle\fontNum = LoadFont(*pCircle\fontNum,*pCircle\newFont,*pCircle\radius / 3 - 1,#PB_Font_Bold)
*pCircle\newFont = "" ;reset so repeated attempts will not be made
If IsFont(*pCircle\fontNum) ;if successfully loaded set the fontID
*pCircle\fontID = FontID(*pCircle\fontNum)
EndIf
EndIf
CallDebugger
StartDrawing(ImageOutput(*pCircle\imageNum))
;handle text first
If text <> ""
If *pCircle\fontID <> 0 ;check if fontID is initialized
DrawingFont(*pCircle\fontID) ;use only successfully loaded font
TH2 = TextHeight(text) / 2
TW2 = TextWidth(text) / 2
; [Demivec] adjusted circle function to use new parameter
Circle(*pCircle\radius + 0.5,*pCircle\radius + 0.5,*pCircle\Size / 3,*pCircle\backgroundcolor);clear text area
DrawText(*pCircle\radius - TW2,*pCircle\radius - TH2,text,*pCircle\textcolor,*pCircle\backgroundcolor)
EndIf
EndIf
;check if setting a range or only a single state
; a range will be set (of from 1 to +state) if state is negative,
; only the single state will be set if state>0
If state<0
rangeStart = 1
state * -1
Else
rangeStart = state
EndIf
CallDebugger
;first segment is at top of circle
state - 1
For m= rangeStart - 1 To state ; To clear the circle (set color to initial color and text to 1 space, and state=-segments) [Demivec] changed explanation
angle_rad = #PI * ((2 * m + 1) / *pCircle\segmentCount - 1/2)
; calculate point on radius
x = *pCircle\radius + (*pCircle\radius - 1) * Cos(angle_rad) ; [Demivec] adjusted calculation, finds a point at a set distance from largest radius
y = *pCircle\radius + (*pCircle\radius - 1) * Sin(angle_rad)
;fill segment with segmentColor, if non-continuos, or small variations of segmentColro if a continuous circle
If *pCircle\style = 1
If m = (*pCircle\segmentCount - 1)
FillArea(x,y,-1,*pCircle\segmentColor + 1 - 2 *(*pCircle\segmentCount & 1)) ;handle color choice for last segment
Else
FillArea(x,y,-1,*pCircle\segmentColor + (m & 1)) ;handle color choice for all but last segment
EndIf
Else
FillArea(x,y,*pCircle\backgroundcolor,*pCircle\segmentColor) ;handle non-continuous circle segment
EndIf
Next
StopDrawing()
SetGadgetState(*pCircle\gadgetNum,ImageID(*pCircle\imageNum)) ;}
Case 0 ;{initialize progress circle image and gadget
;
If *pCircle\imageNum = #PB_Any
*pCircle\imageNum = CreateImage(#PB_Any,*pCircle\Size,*pCircle\Size) ;assign a generated image#
Else
;use image# supplied (frees previous image if necessary)
CreateImage(*pCircle\imageNum,*pCircle\Size,*pCircle\Size)
EndIf
*pCircle\radius = (*pCircle\Size - 1) / 2 ; [Demivec] adjusted calculation based on circle function side-effects
StartDrawing(ImageOutput(*pCircle\imageNum))
Box(0,0,*pCircle\Size,*pCircle\Size,*pCircle\backgroundcolor)
; [Demivec] adjusted circle function to use new parameter
Circle(*pCircle\radius + 0.5,*pCircle\radius + 0.5,*pCircle\radius + 0.5,*pCircle\segmentColor+(*pCircle\style & 1) * 2)
Circle(*pCircle\radius + 0.5,*pCircle\radius + 0.5,*pCircle\Size / 3,*pCircle\backgroundcolor)
LineXY(*pCircle\radius,*pCircle\radius,*pCircle\radius,0,*pCircle\segmentColor)
If *pCircle\style = 1 ;continuous
;draw alternating segments using small variations in color
For m = 1 To *pCircle\segmentCount - 1
angle_rad = #PI * ((2 * m) / *pCircle\segmentCount - 1/2)
x = *pCircle\radius * (1 + Cos(angle_rad))
y = *pCircle\radius * (1 + Sin(angle_rad))
LineXY(*pCircle\radius,*pCircle\radius,x,y,*pCircle\segmentColor + (m & 1))
angle_rad = #PI * ((2 * m - 1) / *pCircle\segmentCount - 1/2)
; calculate point on radius
x2 = *pCircle\radius + (*pCircle\radius - 1) * Cos(angle_rad)
y2 = *pCircle\radius + (*pCircle\radius - 1) * Sin(angle_rad)
FillArea(x2,y2,-1,*pCircle\segmentColor + ((m + 1) & 1))
Next
If (*pCircle\segmentCount & 1) = 1 ;for odd segmentCount last segment gets a unique color
LineXY(*pCircle\radius,*pCircle\radius,x,y,*pCircle\segmentColor - (*pCircle\segmentCount & 1))
EndIf
angle_rad = #PI * ((2 * (*pCircle\segmentCount) - 1) / *pCircle\segmentCount - 1/2)
x = *pCircle\radius + (*pCircle\radius - 1) * Cos(angle_rad)
y = *pCircle\radius + (*pCircle\radius - 1) * Sin(angle_rad)
FillArea(x,y,-1,*pCircle\segmentColor + 1 - 2 *(*pCircle\segmentCount & 1))
Else ;non-continuous
;draw radial lines
For m = 1 To *pCircle\segmentCount - 1
angle_rad = #PI * ((2 * m) / *pCircle\segmentCount - 1/2) ;
x = *pCircle\radius * (1 + Cos(angle_rad))
y = *pCircle\radius * (1 + Sin(angle_rad))
LineXY(*pCircle\radius,*pCircle\radius,x,y,*pCircle\backgroundcolor)
Next
EndIf
StopDrawing()
If *pCircle\gadgetNum = #PB_Any
*pCircle\gadgetNum = ImageGadget(#PB_Any,x,y,*pCircle\Size,*pCircle\Size,ImageID(*pCircle\imageNum))
Else
ImageGadget(*pCircle\gadgetNum,*pCircle\gx,*pCircle\gy,*pCircle\Size,*pCircle\Size,ImageID(*pCircle\imageNum))
EndIf ;}
Case -1 ;{free progress circle elements (image, font, gadget)
If IsGadget(*pCircle\gadgetNum)
FreeGadget(*pCircle\gadgetNum)
*pCircle\gadgetNum = 0
EndIf
If IsFont(*pCircle\fontNum)
FreeFont(*pCircle\fontNum)
*pCircle\fontNum = 0
EndIf
If IsImage(*pCircle\imageNum)
FreeImage(*pCircle\imageNum)
*pCircle\imageNum = 0
EndIf ;}
Case 10 ;{setup default progress circle data, suitable for an init cmd
;gx,gy,textColor values are left unchanged
*pCircle\Size = 50 ;
*pCircle\segmentCount = 25
*pCircle\segmentColor = $AAB68F
*pCircle\backgroundcolor = $D2D8C3
*pCircle\gadgetNum = #PB_Any
*pCircle\imageNum = #PB_Any
*pCircle\fontNum = #PB_Any
*pCircle\fontID = 0
CompilerIf #PB_Compiler_OS = #PB_OS_Windows
*pCircle\newFont = "Arial"
CompilerEndIf
CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
*pCircle\newFont = "Arial"
CompilerEndIf
CompilerIf #PB_Compiler_OS = #PB_OS_Linux
*pCircle\newFont = "FreeSans" ;this is only a guess, suggestions welcome
CompilerEndIf ;}
EndSelect
While WindowEvent():Wend; update the gadget
ProcedureReturn 1
EndProcedure
;---- DEMO -----<
Define.l r,g,b,count,m
Define.l event
OpenWindow(0,0,0,310,310,"ProgressCircle DEMO",#PB_Window_SystemMenu | #PB_Window_SizeGadget)
CreateGadgetList(WindowID(0))
Define.ProgressCircle circle_1,circle_2
;setup progressCircle data
ProgressCircle(@circle_1,10) ;set ProgressCircle defaults
circle_1\gadgetNum = 0 ;#PB_Any
circle_1\gx = 70
circle_1\gy = 70
circle_1\style = 1
ProgressCircle(@circle_1,0) ;initialize progressCircle
r = 10
g = 150
b = 10
count = 0
Repeat
circle_1\segmentColor = RGB(r, g, b)
For m = 1 To circle_1\segmentCount
circle_1\textcolor = RGB(236, 200 - (m * 5), 19)
ProgressCircle(@circle_1,1,m,StrF(m / circle_1\segmentCount * 100,0) + "%") ;update ProgressCircle
Delay(100)
event = WindowEvent()
While event ;improved event catcher for demo
If event = #PB_Event_CloseWindow
Break 2
EndIf
event=WindowEvent()
Wend
Next
count+1
r+20
g+20
b+10
If r >240 Or g>240 Or b>240
r=Random(255)
g=Random(255)
b=Random(255)
EndIf
Until event =#PB_Event_CloseWindow Or count=3
circle_1\segmentColor = $AAB68F
ProgressCircle(@circle_1,1,-circle_1\segmentCount) ; clear the ProgressCircle....
Delay(300)
;ProgressCircle(@circle_1,-1) ;free Progress Circle elements, image,gadget,font [Demivec]
End
it's looking very promising
.. but ... isn't working... (continous circle) I'll get only a green caret... (i'll have a closer look)
The "standard" circle has an issue with the first and last segment... they have no border between them... If I change the line 168 to
it's working again (the segmented circle)... but with a double line on the top (needs some investigation too)
A question: why did you change the var-type to float? (#pi is stored as double... this will cause some inaccuracy besides the "normal" inaccuracy of floats/doubles... is there a particular reason?

The "standard" circle has an issue with the first and last segment... they have no border between them... If I change the line 168 to
Code: Select all
For m = 0 To *pCircle\segmentCount
A question: why did you change the var-type to float? (#pi is stored as double... this will cause some inaccuracy besides the "normal" inaccuracy of floats/doubles... is there a particular reason?
Thanks for correcting that, I had made attempts at sharing more code between the 2 types of circles but didn't code the coloring of the line properly for the segmented circle. It can be corrected by making the change you specified and you should also move Line#144 (the LineXY() function) down between lines #147 and #148. The code from #144 to #149 should then read:walker wrote:it's looking very promising.. but ... isn't working... (continous circle) I'll get only a green caret... (i'll have a closer look)
The "standard" circle has an issue with the first and last segment... they have no border between them... If I change the line 168 to
it's working again (the segmented circle)... but with a double line on the top (needs some investigation too)Code: Select all
For m = 0 To *pCircle\segmentCount
A question: why did you change the var-type to float? (#pi is stored as double... this will cause some inaccuracy besides the "normal" inaccuracy of floats/doubles... is there a particular reason?
Code: Select all
Circle(*pCircle\radius + 0.5,*pCircle\radius + 0.5,*pCircle\Size / 3,*pCircle\backgroundcolor)
If *pCircle\style = 1 ;continuous
;draw alternating segments using small variations in color
LineXY(*pCircle\radius,*pCircle\radius,*pCircle\radius,0,*pCircle\segmentColor)
For m = 1 To *pCircle\segmentCount - 1
angle_rad = #PI * ((2 * m) / *pCircle\segmentCount - 1/2)

The long winded answer is that it was determined that the results of the Sin() function was giving results that weren't desired. For instance Sin(#PI) didn't equal Sin(0). This was due to #PI being rounded. I did extensive testing to determine ways of producing identical results from the Sin() function using those values. They crop up when the segmentCount is evenly divisible by 4. The values should come out equal so that the segments on opposite sides of the circle are on the same horizontal line.
This was accomplished by using a float instead of a double. There is no descernible loss in precision. The size of circles are such that they would be much larger than your screen before you would notice a difference. To demonstrate this examine this code:
Code: Select all
Define x.l,x2.l,y.l,y2.l,angle_rad.f,angle_rad2.d,segmentCount.l,radius.f,m.l,Size.l
Size = 1280
segmentCount = 1000
radius = (Size + 1 ) / 2
For m=0 To segmentCount-1
;using a float
angle_rad = #PI * ((2 * m) / segmentCount - 1/2)
x = radius * (1 + Cos(angle_rad))
y = radius * (1 + Sin(angle_rad))
;using a double
angle_rad2 = #PI * ((2 * m) / segmentCount - 1/2)
x2 = radius * (1 + Cos(angle_rad2))
y2 = radius * (1 + Sin(angle_rad2))
;check for differences
If (x <> x2) Or (y <> y2)
Debug "m= " + Str(m) + ", (" + Str(x) + "," + Str(y) + ") <> (" + Str(x2) + "," + Str(y2) + ")"
EndIf
Next
For a given circle with a segmentCount as large as the size of the circle it can have as many as 9 such differences. But there are no circles smaller than size 365 that have more than 4 such differences. I think they are neglible.
All other inaccuracies in the rendering of the circles is due to the manner that the functions are translated into API functions. For instance, in PureBasic we are specifying a circle with center at (radius,radius) with radius = radius (naturally). For the Windows OS this is translated into a box from (0,0) to (2*radius,2*radius) that contains a circle touching the borders of the box. This makes the radius of the circle that is drawn have a smaller radius than the one specified (by 0.5).
This is evident if you consider that the width of the box containing a circle of radius X would be an odd number, always. In our case it's always even. This is also what makes the Size limited to being an even number. If it was odd the circle drawn would be either too large for the box or too small (but still an even size). These limitations could not be resolved without drawing the circle entirely through code, without using the PB function. I thought that wasn't the route to take.
-
- Enthusiast
- Posts: 480
- Joined: Thu Jul 27, 2006 4:06 am
Sorry I never meant to use GIF but a series of frames, perhaps in PNG with alpha channel - that's how I did it at least and it works just fine, even Nero does it (but let's face it, they bloated their software long time ago; not necessarily with this though).
I also use vector graphics for my source data, out of convenience. There are certain technologies that Microsoft offers you to handle the skinning / theming of applications in a nice way for both programmers and artists.
I also use vector graphics for my source data, out of convenience. There are certain technologies that Microsoft offers you to handle the skinning / theming of applications in a nice way for both programmers and artists.


@superadnim: would you give an example? Or more specifically would you show a simple set of images possibly with skinning / theming and display them repetively in a imageGadget or something? I would find it useful to know how the process could be carried out. A brief example would go a long way. The idea would be useful to see..superadnim wrote:Sorry I never meant to use GIF but a series of frames, perhaps in PNG with alpha channel - that's how I did it at least and it works just fine, even Nero does it (but let's face it, they bloated their software long time ago; not necessarily with this though).
I also use vector graphics for my source data, out of convenience. There are certain technologies that Microsoft offers you to handle the skinning / theming of applications in a nice way for both programmers and artists.