help please with creating a graphics component

Just starting out? Need help? Post your questions and find answers here.
doctornash
Enthusiast
Enthusiast
Posts: 130
Joined: Thu Oct 20, 2011 7:22 am

help please with creating a graphics component

Post by doctornash »

Happy '012 all! :D

One of the objects I use the most in my VB audio applications is an 'envelope control'. Referring to the below screenie:

1. It consists of 5 nodes joined by line segments, and the default appearance of the component is as shown in 'Filter Cutoff Synth Wave 1'
2. If the user clicks approximately on a node, drags the mouse to another location and releases, the node moves to the point of mouse release and the line segments redraw to the new point. The 'Volume Envelope Sample' and 'Volume Envelope Synth Wave 1' show outcomes of a user having done this
3. The outermost nodes move only along the vertical axis (If the user moves the node as per 2, the new X position is ignored, and the node moves only to the new Y position)
4. No node can move past the position of another node, as shown by 'Frequency Envelope Synth Wave 1'

Image

I am seeking your assistance in creating such a component in PB. Doing so will play a big part in my being able to move across to PB for future audio projects (plus practically teach me about '2D PB user interactive graphics'). For your reference, here is the relevant VB6 source I put together for my current component:

First, put a Picture box on a form: Picture1
Set the following properties:
AutoRedraw = True
ScaleMode = 0-User (not pixel, twip, point, millimeter etc)

Then:

Code: Select all

Dim Drawing1 As Boolean
Dim StartX1 As Integer
Dim StartY1 As Integer

Dim Drawing2 As Boolean
Dim StartX2 As Integer
Dim StartY2 As Integer

Dim Drawing3 As Boolean
Dim StartX3 As Integer
Dim StartY3 As Integer

Dim Drawing4 As Boolean
Dim StartX4 As Integer
Dim StartY4 As Integer

Dim Drawing5 As Boolean
Dim StartX5 As Integer
Dim StartY5 As Integer

Function ADSR(ByVal xUnit As Long, ByVal NumxDvns As Long) As Double
Dim ScaledxUnit As Double
ScaledxUnit = (xUnit * (1000 / NumxDvns))
If ScaledxUnit <= StartX2 Then
ADSR = (StartY1 + ((StartY2 - StartY1) / (StartX2 - StartX1)) * (xUnit * (1000 / NumxDvns) - StartX1)) / 1000
ElseIf ScaledxUnit > StartX2 And ScaledxUnit <= StartX3 Then
ADSR = (StartY2 + ((StartY3 - StartY2) / (StartX3 - StartX2)) * (xUnit * (1000 / NumxDvns) - StartX2)) / 1000
ElseIf ScaledxUnit > StartX3 And ScaledxUnit <= StartX4 Then
ADSR = (StartY3 + ((StartY4 - StartY3) / (StartX4 - StartX3)) * (xUnit * (1000 / NumxDvns) - StartX3)) / 1000
ElseIf ScaledxUnit > StartX4 And ScaledxUnit <= StartX5 Then
ADSR = (StartY4 + ((StartY5 - StartY4) / (StartX5 - StartX4)) * (xUnit * (1000 / NumxDvns) - StartX4)) / 1000
End If
End Function

Private Sub Form_Load()
Picture1.Scale (0, 1000)-(1000, 0)
StartX1 = 0
StartY1 = 500

StartX2 = 250
StartY2 = 500

StartX3 = 500
StartY3 = 500

StartX4 = 750
StartY4 = 500

StartX5 = 1000
StartY5 = 500
End Sub

Private Sub Form_Activate()
Picture1.FillStyle = vbFSSolid
Picture1.FillColor = vbRed
Picture1.Circle (StartX1, StartY1), 20, vbRed
Picture1.Circle (StartX2, StartY2), 20, vbRed
Picture1.Circle (StartX3, StartY3), 20, vbRed
Picture1.Circle (StartX4, StartY4), 20, vbRed
Picture1.Circle (StartX5, StartY5), 20, vbRed
Picture1.Line (StartX2, StartY2)-(StartX1, StartY1), vbRed
Picture1.Line (StartX3, StartY3)-(StartX2, StartY2), vbRed
Picture1.Line (StartX4, StartY4)-(StartX3, StartY3), vbRed
Picture1.Line (StartX5, StartY5)-(StartX4, StartY4), vbRed
End Sub

Private Sub Picture1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
If X <= StartX1 + 35 And Y >= StartY1 - 35 And Y <= StartY1 + 35 Then
Drawing1 = True
End If
If X >= StartX2 - 35 And X <= StartX2 + 35 And Y >= StartY2 - 35 And Y <= StartY2 + 35 Then
Drawing2 = True
End If
If X >= StartX3 - 35 And X <= StartX3 + 35 And Y >= StartY3 - 35 And Y <= StartY3 + 35 Then
Drawing3 = True
End If
If X >= StartX4 - 35 And X <= StartX4 + 35 And Y >= StartY4 - 35 And Y <= StartY4 + 35 Then
Drawing4 = True
End If
If X >= StartX5 - 35 And Y >= StartY5 - 35 And Y <= StartY5 + 35 Then
Drawing5 = True
End If
End Sub

Private Sub Picture1_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
If Drawing1 = True Then
Drawing1 = False
StartX1 = 0
StartY1 = Int(Y)
    If StartY1 > 1000 Then
    StartY1 = 1000
    ElseIf StartY1 < 0 Then
    StartY1 = 0
    End If
ReDoPici
End If
If Drawing2 = True Then
Drawing2 = False
StartX2 = Int(X)
StartY2 = Int(Y)
    If StartX2 > StartX3 Then
    StartX2 = StartX3
    ElseIf StartX2 < 20 Then
    StartX2 = 20
    End If
    If StartY2 > 1000 Then
    StartY2 = 1000
    ElseIf StartY2 < 0 Then
    StartY2 = 0
    End If
ReDoPici
End If
If Drawing3 = True Then
Drawing3 = False
StartX3 = Int(X)
StartY3 = Int(Y)
    If StartX3 < StartX2 Then
    StartX3 = StartX2
    ElseIf StartX3 > StartX4 Then
    StartX3 = StartX4
    End If
    If StartY3 > 1000 Then
    StartY3 = 1000
    ElseIf StartY3 < 0 Then
    StartY3 = 0
    End If
ReDoPici
End If
If Drawing4 = True Then
Drawing4 = False
StartX4 = Int(X)
StartY4 = Int(Y)
    If StartX4 < StartX3 Then
    StartX4 = StartX3
    ElseIf StartX4 > 980 Then
    StartX4 = 980
    End If
    If StartY4 > 1000 Then
    StartY4 = 1000
    ElseIf StartY4 < 0 Then
    StartY4 = 0
    End If
ReDoPici
End If
If Drawing5 = True Then
Drawing5 = False
StartX5 = 1000
StartY5 = Int(Y)
    If StartY5 > 1000 Then
    StartY5 = 1000
    ElseIf StartY5 < 0 Then
    StartY5 = 0
    End If
ReDoPici
End If
End Sub

Sub ReDoPici()
Picture1.Cls
Picture1.FillStyle = vbFSSolid
Picture1.FillColor = vbRed
Picture1.Circle (StartX1, StartY1), 20, vbRed
Picture1.Circle (StartX2, StartY2), 20, vbRed
Picture1.Circle (StartX3, StartY3), 20, vbRed
Picture1.Circle (StartX4, StartY4), 20, vbRed
Picture1.Circle (StartX5, StartY5), 20, vbRed
Picture1.Line (StartX2, StartY2)-(StartX1, StartY1), vbRed
Picture1.Line (StartX3, StartY3)-(StartX2, StartY2), vbRed
Picture1.Line (StartX4, StartY4)-(StartX3, StartY3), vbRed
Picture1.Line (StartX5, StartY5)-(StartX4, StartY4), vbRed
End Sub
infratec
Always Here
Always Here
Posts: 7622
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: help please with creating a graphics component

Post by infratec »

Hi,

you should search here for new gadgets with the canvas gadget.

A first start

Code: Select all

Structure FilterBoxGadgetStr
  DataPoint.POINTS[5]
EndStructure

Global NewMap FilterBoxGadgetMap.FilterBoxGadgetStr()

Procedure.i FilterBoxGadget(No.i, x.i, y.i, width.i, height.i)
  
  ;Protected *FilterBoxGadget.FilterBoxGadgetStr
  ;
  ;*FilterBoxGadget = AllocateMemory(SizeOf(FilterBoxGadgetStr))
  ;SetGadgetData(GadgetID, *FilterBoxGadget)

  
  If No = #PB_Any Or IsGadget(No) = 0
    No = CanvasGadget(#PB_Any, x, y, width, height, #PB_Canvas_Border)
    AddMapElement(FilterBoxGadgetMap(), Str(No))
    For i = 0 To 4
      FilterBoxGadgetMap()\DataPoint[i]\x = ((width - 10) / 4) * i
      FilterBoxGadgetMap()\DataPoint[i]\y = height / 2
    Next i
  EndIf
  
  If FindMapElement(FilterBoxGadgetMap(), Str(No))
    If StartDrawing(CanvasOutput(No))
      
      For i = 0 To 4
        Circle(FilterBoxGadgetMap()\DataPoint[i]\x, FilterBoxGadgetMap()\DataPoint[i]\y, 5, $0000FF)
        If i < 4
          LineXY(FilterBoxGadgetMap()\DataPoint[i]\x, FilterBoxGadgetMap()\DataPoint[i]\y, FilterBoxGadgetMap()\DataPoint[i + 1]\x, FilterBoxGadgetMap()\DataPoint[i + 1]\y,$0000FF)
        EndIf
      Next i
      StopDrawing()
    EndIf
  EndIf
  
  ProcedureReturn No
  
EndProcedure




OpenWindow(0, 0, 0, 400, 300, "Test", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)

No = FilterBoxGadget(#PB_Any, 10, 10, 200, 100)

Exit = #False
Repeat
  
  Event = WaitWindowEvent()
  
  Select Event
    Case #PB_Event_CloseWindow
      Exit = #True
  EndSelect
  
Until Exit
But now I have to sleep.

Good night.
doctornash
Enthusiast
Enthusiast
Posts: 130
Joined: Thu Oct 20, 2011 7:22 am

Re: help please with creating a graphics component

Post by doctornash »

...my PB version is 4.51. I have to upgrade to 4.6 to be able to use the canvas gadget?
Ramihyn_
Enthusiast
Enthusiast
Posts: 314
Joined: Fri Feb 24, 2006 9:40 am

Re: help please with creating a graphics component

Post by Ramihyn_ »

doctornash wrote:...my PB version is 4.51. I have to upgrade to 4.6 to be able to use the canvas gadget?
Yes.
User avatar
Demivec
Addict
Addict
Posts: 4270
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: help please with creating a graphics component

Post by Demivec »

doctornash wrote:4. No node can move past the position of another node, as shown by 'Frequency Envelope Synth Wave 1'
I have a question regarding the extreme types of node positioning that you would allow. If the dimension of the picture was 20 pixels by 50 pixels would you allow positioning a group of five nodes in the following positions n1(0,10); n2(0, 15); n3(0, 5); n4(0, 18); n5(50, 10)?
infratec
Always Here
Always Here
Posts: 7622
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: help please with creating a graphics component

Post by infratec »

doctornash wrote:...my PB version is 4.51. I have to upgrade to 4.6 to be able to use the canvas gadget?
Yes.

But that's free.

Bernd
doctornash
Enthusiast
Enthusiast
Posts: 130
Joined: Thu Oct 20, 2011 7:22 am

Re: help please with creating a graphics component

Post by doctornash »

I have a question regarding the extreme types of node positioning that you would allow. If the dimension of the picture was 20 pixels by 50 pixels would you allow positioning a group of five nodes in the following positions n1(0,10); n2(0, 15); n3(0, 5); n4(0, 18); n5(50, 10)?
I tried pushing the nodes to the extreme positions you've suggested, and the below resulted:
Image
The thing to note about n2, n3, n4 is that the code does not allow them to be centred on x = 0 (unlike n1, which is always centred on x=0). They are all in line with each other, but they cannot be pushed to the left less than say x = 2 (on your suggested scale). Similarly, n5 is the only one that is centred on x = 50; all other nodes cannot be pushed to the right more than say = 48 (on your suggested scale)
infratec
Always Here
Always Here
Posts: 7622
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: help please with creating a graphics component

Post by infratec »

Hi doctornash,

stage 2:
Save it as FilterBoxGadget.pbi

Code: Select all

Structure FilterBoxGadgetStr
  DataPoint.Point[5]
  Disabled.i
  ActivePoint.i
EndStructure


Procedure FilterBoxGadgetDraw(GadgetNo.i, Color.i = $FFFFFF)
  
  Protected *FilterBoxGadget.FilterBoxGadgetStr
  Protected i.i
  
  If IsGadget(GadgetNo)
    *FilterBoxGadget = GetGadgetData(GadgetNo)
    If StartDrawing(CanvasOutput(GadgetNo))
      Box(0, 0, GadgetWidth(GadgetNo), GadgetHeight(GadgetNo), Color)
      For i = 0 To 4
        Circle(*FilterBoxGadget\DataPoint[i]\x, *FilterBoxGadget\DataPoint[i]\y, 5, $0000FF)
        If i < 4
          LineXY(*FilterBoxGadget\DataPoint[i]\x, *FilterBoxGadget\DataPoint[i]\y, *FilterBoxGadget\DataPoint[i + 1]\x, *FilterBoxGadget\DataPoint[i + 1]\y, $0000FF)
        EndIf
      Next i
      StopDrawing()
    EndIf
  EndIf
  
EndProcedure


Procedure.i FilterBoxGadget(GadgetNo.i, x.i, y.i, width.i, height.i)
  
  Protected *FilterBoxGadget.FilterBoxGadgetStr
  Protected i.i
  
  If GadgetNo = #PB_Any Or IsGadget(GadgetNo) = 0
    GadgetNo = CanvasGadget(#PB_Any, x, y, width, height, #PB_Canvas_Border)
    If GadgetNo
      *FilterBoxGadget = AllocateMemory(SizeOf(FilterBoxGadgetStr))
      SetGadgetData(GadgetNo, *FilterBoxGadget)
      For i = 0 To 4
        *FilterBoxGadget\DataPoint[i]\x = ((width - 5) / 4) * i
        *FilterBoxGadget\DataPoint[i]\y = height / 2
      Next i
      *FilterBoxGadget\Disabled = #False
      *FilterBoxGadget\ActivePoint = -1
    EndIf
  EndIf
  
  FilterBoxGadgetDraw(GadgetNo)
  
  ProcedureReturn GadgetNo
  
EndProcedure


Procedure.i FilterBoxGadgetEvent(GadgetNo)
  
  Protected *FilterBoxGadget.FilterBoxGadgetStr
  Protected Result.i
  Protected XPos.i, YPos.i
  Protected i.i, EventT
  
  Result = 0
  
  *FilterBoxGadget = GetGadgetData(GadgetNo)

  If Not *FilterBoxGadget\Disabled
    XPos = GetGadgetAttribute(GadgetNo, #PB_Canvas_MouseX)
    YPos = GetGadgetAttribute(GadgetNo, #PB_Canvas_MouseY)
    
    If XPos < 0 : XPos = 0 : EndIf
    If XPos > GadgetWidth(GadgetNo) : XPos = GadgetWidth(GadgetNo) : EndIf
    If YPos < 0 : YPos = 0 : EndIf
    If YPos > GadgetHeight(GadgetNo) - 4 : YPos = GadgetHeight(GadgetNo) - 4 : EndIf
    
    EventT = EventType()
    
    If EventT = #PB_EventType_LeftButtonDown And *FilterBoxGadget\ActivePoint = -1
      For i = 0 To 4
        If (*FilterBoxGadget\DataPoint[i]\x - 5) < XPos And (*FilterBoxGadget\DataPoint[i]\x + 5) > XPos
          If (*FilterBoxGadget\DataPoint[i]\y - 5) < YPos And (*FilterBoxGadget\DataPoint[i]\y + 5) > YPos
            *FilterBoxGadget\ActivePoint = i
            Break
          EndIf
        EndIf
      Next i
    EndIf
    
    If *FilterBoxGadget\ActivePoint <> -1
      If *FilterBoxGadget\ActivePoint > 0 And *FilterBoxGadget\ActivePoint < 4
        If XPos >= *FilterBoxGadget\DataPoint[*FilterBoxGadget\ActivePoint - 1]\x
          If XPos <= *FilterBoxGadget\DataPoint[*FilterBoxGadget\ActivePoint + 1]\x
            *FilterBoxGadget\DataPoint[*FilterBoxGadget\ActivePoint]\x = XPos
            *FilterBoxGadget\DataPoint[*FilterBoxGadget\ActivePoint]\y = YPos
          EndIf
        EndIf
      Else
        *FilterBoxGadget\DataPoint[*FilterBoxGadget\ActivePoint]\y = YPos
      EndIf
      FilterBoxGadgetDraw(GadgetNo)
    EndIf
    
    If EventT = #PB_EventType_LeftButtonUp And *FilterBoxGadget\ActivePoint <> -1
      *FilterBoxGadget\ActivePoint = -1
      Result = 1
    EndIf
    
  EndIf
  
  ProcedureReturn Result
  
EndProcedure


Procedure DisableFilterBoxGadget(GadgetNo, State)

  Protected *FilterBoxGadget.FilterBoxGadgetStr
  
  *FilterBoxGadget= GetGadgetData(GadgetNo)
  If State
    If Not *FilterBoxGadget\Disabled
      *FilterBoxGadget\Disabled = #True
      FilterBoxGadgetDraw(GadgetNo, $DFDFDF)
    EndIf
  Else
    If *FilterBoxGadget\Disabled
      *FilterBoxGadget\Disabled = #False
      FilterBoxGadgetDraw(GadgetNo)
    EndIf
  EndIf
  
EndProcedure


Procedure.i FilterBoxGadgetGetState(GadgetNo, Array Test.Point(1))
  
  Protected *FilterBoxGadget.FilterBoxGadgetStr
  Protected Result.i, i.i
  
  Result = #False
  
  If IsGadget(GadgetNo)
    *FilterBoxGadget = GetGadgetData(GadgetNo)
    For i = 0 To 4
      Test(i)\x = *FilterBoxGadget\DataPoint[i]\x
      Test(i)\y = *FilterBoxGadget\DataPoint[i]\y
    Next i
    Result = #True
  EndIf
  
  ProcedureReturn Result
  
EndProcedure
To test:

Code: Select all

IncludeFile "FilterBoxGadget.pbi"

Dim Filter.Point(4)

OpenWindow(0, 0, 0, 430, 340, "Test", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)

No1 = FilterBoxGadget(#PB_Any, 10, 10, 200, 100)
No2 = FilterBoxGadget(#PB_Any, 10, 120, 200, 100)
No3 = FilterBoxGadget(#PB_Any, 220, 120, 200, 100)
No4 = FilterBoxGadget(#PB_Any, 10, 230, 200, 100)
No5 = FilterBoxGadget(#PB_Any, 220, 230, 200, 100)

Exit = #False
Repeat
  
  Event = WaitWindowEvent()
  
  Select Event
    Case #PB_Event_Gadget
      Select EventGadget()
        Case No1
          If FilterBoxGadgetEvent(No1) = 1
            If FilterBoxGadgetGetState(No1, Filter())
              For i = 0 To 4
                Debug "P" + Str(i + 1) + " (" + Str(Filter(i)\x) + "/" + Str(Filter(i)\y) + ")"
              Next i
            EndIf
          EndIf
        Case No2 : FilterBoxGadgetEvent(No2)
        Case No3 : FilterBoxGadgetEvent(No3)
        Case No4 : FilterBoxGadgetEvent(No4)
        Case No5 : FilterBoxGadgetEvent(No5)
      EndSelect
    Case #PB_Event_CloseWindow
      Exit = #True
  EndSelect
  
Until Exit
Bernd
Last edited by infratec on Mon Jan 02, 2012 9:14 pm, edited 2 times in total.
Little John
Addict
Addict
Posts: 4791
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: help please with creating a graphics component

Post by Little John »

Not bad! :-) Image
(But the both outer nodes cannot be moved up and down, can they?)

Regards, Little John
infratec
Always Here
Always Here
Posts: 7622
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: help please with creating a graphics component

Post by infratec »

Little John wrote:(But the both outer nodes cannot be moved up and down, can they?)
Hi, hi,

first I fixed the outer points,
(for i = 1 to 3 instead of 0 to 4)
but than I looked at the picture in the first posting. :D
They are movable in the y-axis.

Bernd
Little John
Addict
Addict
Posts: 4791
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: help please with creating a graphics component

Post by Little John »

Sorry, my fault.
Previously, I didn't hit the outer nodes exactly.
If I'm clicking more precisely, then it works. :-)

Regards, Little John
doctornash
Enthusiast
Enthusiast
Posts: 130
Joined: Thu Oct 20, 2011 7:22 am

Re: help please with creating a graphics component

Post by doctornash »

WOW, ALMOST perfect! :D

The only two minor things are (and btw I only addressed these after the initial construction of the component in VB when I discovered user issues; you will see implementation for these in the source I posted):

1. The user has to click absolutely on the node for it to move. I had allowed for a little 'region around each node' for the click to be registered, so that even if the user 'approximately' clicked near the node, it would still move. This was so the user wouldn't have to try to move it multiple times.

2. It should not be possible for Nodes 2, 3, 4 to be centred on the extreme left and right vertical axes. They should only be able to go as far as to 'touch' those axes. Only Nodes 1 and 5 are permanently centred on those axes. I try to show this in the second screenshot I posted

Regards,
Jim
User avatar
Demivec
Addict
Addict
Posts: 4270
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: help please with creating a graphics component

Post by Demivec »

doctornash wrote:WOW, ALMOST perfect! :D

The only two minor things are (and btw I only addressed these after the initial construction of the component in VB when I discovered user issues; you will see implementation for these in the source I posted):

1. The user has to click absolutely on the node for it to move. I had allowed for a little 'region around each node' for the click to be registered, so that even if the user 'approximately' clicked near the node, it would still move. This was so the user wouldn't have to try to move it multiple times.

2. It should not be possible for Nodes 2, 3, 4 to be centred on the extreme left and right vertical axes. They should only be able to go as far as to 'touch' those axes. Only Nodes 1 and 5 are permanently centred on those axes. I try to show this in the second screenshot I posted
I made some modifcations to infratec's code. It handles item #2 according to your needs. It handles item #1 by changing the mouse cursor (pointer) to a hand when over a node and the left mouse button is pushed. The changing of the mouse cursor means a user should only have to push the left mouse button to see if they are on the point they want to move. If the mouse cursor doesn't change then they need to reposition the mouse.

One more change I made was to the event handling for repositioning a node by implementing the MouseMove event. I think it functions much better.

My apologies to infratec for any changes in capitalization that I made. :mrgreen: Here's the modified code:

Code: Select all

;Program: FilterBoxGadget.pbi
;Author: intratec, modified by Demivec
;version: 2

#FilterBoxPointCount = 5
#FilterBoxPointRadius = 5
Structure filterBoxGadgetStr
  dataPoint.POINT[#FilterBoxPointCount]
  width.i
  height.i
  disabled.i
  activePoint.i
EndStructure


Procedure filterBoxGadgetDraw(gadgetNo, color = $FFFFFF)
  
  Protected *filterBoxGadget.filterBoxGadgetStr
  Protected i
  
  If IsGadget(gadgetNo)
    *filterBoxGadget = GetGadgetData(gadgetNo)
    If StartDrawing(CanvasOutput(gadgetNo))
        Box(0, 0, OutputWidth(), OutputHeight(), color)
        For i = 0 To #FilterBoxPointCount - 1
          Circle(*filterBoxGadget\dataPoint[i]\x, *filterBoxGadget\dataPoint[i]\y, #FilterBoxPointRadius, $0000FF)
          If i < #FilterBoxPointCount - 1
            LineXY(*filterBoxGadget\dataPoint[i]\x, *filterBoxGadget\dataPoint[i]\y, *filterBoxGadget\dataPoint[i + 1]\x, *filterBoxGadget\dataPoint[i + 1]\y, $0000FF)
          EndIf
        Next
      StopDrawing()
    EndIf
  EndIf
  
EndProcedure


Procedure filterBoxGadget(gadgetNo, x, y, width, height)
  
  Protected *filterBoxGadget.filterBoxGadgetStr
  Protected i
  
  If gadgetNo = #PB_Any Or IsGadget(gadgetNo) = 0
    gadgetNo = CanvasGadget(#PB_Any, x, y, width, height, #PB_Canvas_Border) 
    If gadgetNo
      *filterBoxGadget = AllocateMemory(SizeOf(filterBoxGadgetStr))
      SetGadgetData(gadgetNo, *filterBoxGadget)
      
      ;gadget output will be smaller if borders are drawn
      StartDrawing(CanvasOutput(gadgetNo))
        width = OutputWidth()
        height = OutputHeight()
      StopDrawing()
      With *filterBoxGadget
        \width = width
        \height = height
        \dataPoint[0]\x = 0
        \dataPoint[0]\y = height / 2
        For i = 1 To #FilterBoxPointCount - 2
          \dataPoint[i]\x = ((0.0 + width - #FilterBoxPointRadius) / (#FilterBoxPointCount - 1)) * i
          \dataPoint[i]\y = height / 2
        Next
        \dataPoint[#FilterBoxPointCount - 1]\x = width - 1
        \dataPoint[#FilterBoxPointCount - 1]\y = height / 2
  
        \disabled = #False
        \activePoint = -1
      EndWith
    EndIf
  EndIf
  
  filterBoxGadgetDraw(gadgetNo)
  
  ProcedureReturn gadgetNo
  
EndProcedure


Procedure filterBoxGadgetEvent(gadgetNo)
  
  Protected *filterBoxGadget.filterBoxGadgetStr
  Protected result
  Protected xPos, yPos
  Protected i, eventT
  
  result = 0
  
  *filterBoxGadget = GetGadgetData(gadgetNo)
  
  With *filterBoxGadget
    If Not \disabled
      xPos = GetGadgetAttribute(gadgetNo, #PB_Canvas_MouseX)
      yPos = GetGadgetAttribute(gadgetNo, #PB_Canvas_MouseY)
      
      If xPos < 0 : xPos = 0 : EndIf
      If xPos > \width - 1: xPos = \width - 1: EndIf
      If yPos < 0 : yPos = 0 : EndIf
      If yPos > \height - 1 : yPos = \height - 1 : EndIf
      
      eventT = EventType()
    
      Select eventT
        Case #PB_EventType_LeftButtonDown
          If \activePoint = -1
            For i = 0 To #FilterBoxPointCount - 1
              If (\dataPoint[i]\x - #FilterBoxPointRadius) < xPos And (\dataPoint[i]\x + #FilterBoxPointRadius) > xPos
                If (\dataPoint[i]\y - #FilterBoxPointRadius) < yPos And (\dataPoint[i]\y + #FilterBoxPointRadius) > yPos
                  \activePoint = i
                  SetGadgetAttribute(gadgetNo, #PB_Canvas_Cursor, #PB_Cursor_Hand)
                  Break
                EndIf
              EndIf
            Next
          EndIf
        Case #PB_EventType_MouseMove
          If \activePoint <> -1
            \dataPoint[\activePoint]\y = yPos
            If \activePoint > 0 And \activePoint < #FilterBoxPointCount - 1
              If xPos = 0: xPos = 1: EndIf
              If xPos = \width - 1: xPos - 1: EndIf
              
              If xPos >= \dataPoint[\activePoint - 1]\x
                If xPos <= \dataPoint[\activePoint + 1]\x
                  \dataPoint[\activePoint]\x = xPos
                Else
                  \dataPoint[\activePoint]\x = \dataPoint[\activePoint + 1]\x
                EndIf 
              Else
                \dataPoint[\activePoint]\x = \dataPoint[\activePoint - 1]\x
              EndIf 
            EndIf
            
            filterBoxGadgetDraw(gadgetNo)
          EndIf
        Case  #PB_EventType_LeftButtonUp
          If \activePoint <> -1
            \activePoint = -1
            SetGadgetAttribute(gadgetNo, #PB_Canvas_Cursor, #PB_Cursor_Default)
            result = 1
          EndIf
       EndSelect
    EndIf
  EndWith
  
  ProcedureReturn result
  
EndProcedure


Procedure disableFilterBoxGadget(gadgetNo, state)
  
  Protected *filterBoxGadget.filterBoxGadgetStr
  
  *filterBoxGadget= GetGadgetData(gadgetNo)
  If state
    If Not *filterBoxGadget\disabled
      *filterBoxGadget\disabled = #True
      filterBoxGadgetDraw(gadgetNo, $DFDFDF)
    EndIf
  Else
    If *filterBoxGadget\disabled
      *filterBoxGadget\disabled = #False
      filterBoxGadgetDraw(gadgetNo)
    EndIf
  EndIf
  
EndProcedure


Procedure filterBoxGadgetGetState(gadgetNo, Array dataPoint.POINT(1))
  
  Protected *filterBoxGadget.filterBoxGadgetStr
  Protected result, i
  
  result = #False
  
  If IsGadget(gadgetNo)
    *filterBoxGadget = GetGadgetData(gadgetNo)
    Dim dataPoint(#FilterBoxPointCount - 1)
    For i = 0 To #FilterBoxPointCount - 1
      dataPoint(i)\x = *filterBoxGadget\dataPoint[i]\x
      dataPoint(i)\y = *filterBoxGadget\dataPoint[i]\y
    Next
    result = #True
  EndIf
  
  ProcedureReturn result
  
EndProcedure
Test code (only change was the addition of a new defined constant):

Code: Select all

XIncludeFile "FilterBoxGadget.pbi"

Dim filter.POINT(#FilterBoxPointCount - 1)

OpenWindow(0, 0, 0, 430, 340, "Test", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)

no1 = FilterBoxGadget(#PB_Any, 10, 10, 200, 100)
no2 = FilterBoxGadget(#PB_Any, 10, 120, 200, 100)
no3 = FilterBoxGadget(#PB_Any, 220, 120, 200, 100)
no4 = FilterBoxGadget(#PB_Any, 10, 230, 200, 100)
no5 = FilterBoxGadget(#PB_Any, 220, 230, 200, 100)

exit = #False
Repeat
  
  event = WaitWindowEvent()
  
  Select event
    Case #PB_Event_Gadget
      Select EventGadget()
        Case no1
          If FilterBoxGadgetEvent(no1) = 1
            If FilterBoxGadgetGetState(no1, filter())
              For i = 0 To #FilterBoxPointCount - 1
                Debug "P" + Str(i + 1) + " (" + Str(filter(i)\x) + "/" + Str(filter(i)\y) + ")"
              Next i
            EndIf
          EndIf
        Case no2 : FilterBoxGadgetEvent(no2)
        Case no3 : FilterBoxGadgetEvent(no3)
        Case no4 : FilterBoxGadgetEvent(no4)
        Case no5 : FilterBoxGadgetEvent(no5)
      EndSelect
    Case #PB_Event_CloseWindow
      exit = #True
  EndSelect
  
Until exit
For my own purposes I have also made a version of the code that allows specifying a different number of points as as a specific point radius to be used for each FilterBoxGadget. I was working on one additional modification, again for my own purposes, that would show a popup menu for a right-click on a line that would allow adding or removing points.


@Edit: Removed stray control character from code. Thanks infratec.
Last edited by Demivec on Tue Jan 03, 2012 3:22 pm, edited 1 time in total.
infratec
Always Here
Always Here
Posts: 7622
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: help please with creating a graphics component

Post by infratec »

Hi Demivec,

all improvements are welcome :D

But you have a typo in your listing which avoids compilation.
(Line 139 before EndSelect is a control byte (DC1))

Also I modified this procedure a bit to the needs (if I think right) of doctornash:

Code: Select all

Procedure filterBoxGadgetEvent(gadgetNo)
 
  Protected *filterBoxGadget.filterBoxGadgetStr
  Protected result
  Protected xPos, yPos
  Protected i
 
  result = 0
 
  *filterBoxGadget = GetGadgetData(gadgetNo)
 
  With *filterBoxGadget
    If Not \disabled
      xPos = GetGadgetAttribute(gadgetNo, #PB_Canvas_MouseX)
      yPos = GetGadgetAttribute(gadgetNo, #PB_Canvas_MouseY)
     
      If xPos < 0 : xPos = 0 : EndIf
      If xPos > \width - 1: xPos = \width - 1: EndIf
      If yPos < 0 : yPos = 0 : EndIf
      If yPos > \height - 1 : yPos = \height - 1 : EndIf
   
      Select EventType()
        Case #PB_EventType_LeftButtonDown
          If \activePoint = -1
            For i = 0 To #FilterBoxPointCount - 1
              If (\dataPoint[i]\x - #FilterBoxPointRadius) < xPos And (\dataPoint[i]\x + #FilterBoxPointRadius) > xPos
                If (\dataPoint[i]\y - #FilterBoxPointRadius) < yPos And (\dataPoint[i]\y + #FilterBoxPointRadius) > yPos
                  \activePoint = i
                  SetGadgetAttribute(gadgetNo, #PB_Canvas_Cursor, #PB_Cursor_Hand)
                  Break
                EndIf
              EndIf
            Next
          EndIf
        Case #PB_EventType_MouseMove
          If \activePoint <> -1
            \dataPoint[\activePoint]\y = yPos
            If \activePoint > 0 And \activePoint < #FilterBoxPointCount - 1
              If xPos < #FilterBoxPointRadius: xPos = #FilterBoxPointRadius: EndIf
              If xPos > \width - #FilterBoxPointRadius: xPos = \width - #FilterBoxPointRadius: EndIf
             
              If xPos >= \dataPoint[\activePoint - 1]\x
                If xPos <= \dataPoint[\activePoint + 1]\x
                  \dataPoint[\activePoint]\x = xPos
                Else
                  \dataPoint[\activePoint]\x = \dataPoint[\activePoint + 1]\x
                EndIf
              Else
                \dataPoint[\activePoint]\x = \dataPoint[\activePoint - 1]\x
              EndIf
            EndIf
           
            filterBoxGadgetDraw(gadgetNo)
          EndIf
        Case #PB_EventType_LeftButtonUp
          If \activePoint <> -1
            \activePoint = -1
            SetGadgetAttribute(gadgetNo, #PB_Canvas_Cursor, #PB_Cursor_Default)
            result = 1
          EndIf
      EndSelect
    EndIf
  EndWith
 
  ProcedureReturn result
 
EndProcedure
So please simply replace this procedure in Demivec's code.

@doctornash
If you still want to increase the range of the 'pick up area', you can modify the lines

Code: Select all

If (\dataPoint[i]\x - #FilterBoxPointRadius) < xPos And (\dataPoint[i]\x + #FilterBoxPointRadius) > xPos
                If (\dataPoint[i]\y - #FilterBoxPointRadius) < yPos And (\dataPoint[i]\y + #FilterBoxPointRadius) > yPos
Simply add a value to #FilterBoxPointRadius. Example

Code: Select all

(#FilterBoxPointRadius + 5)
Bernd
doctornash
Enthusiast
Enthusiast
Posts: 130
Joined: Thu Oct 20, 2011 7:22 am

Re: help please with creating a graphics component

Post by doctornash »

guys, I salute your talent and helpfulness. speechless! :shock:
Post Reply