VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
  Persistable = 0  'NotPersistable
  DataBindingBehavior = 0  'vbNone
  DataSourceBehavior  = 0  'vbNone
  MTSTransactionMode  = 0  'NotAnMTSObject
END
Attribute VB_Name = "cMaskCircle"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Option Explicit

Private WithEvents ROuter As cControlPoint
Attribute ROuter.VB_VarHelpID = -1
Private WithEvents LightSourceCenter As cControlPoint
Attribute LightSourceCenter.VB_VarHelpID = -1
Private WithEvents CenterDrag As cControlPoint
Attribute CenterDrag.VB_VarHelpID = -1

Public Sub Init(ControlPoints As cControlPoints, ByVal X As Double, ByVal Y As Double, ByVal RadiusOuter As Double, ByVal LightSourceOffsetXY As Double)
  Set ROuter = ControlPoints.Add("ROuter" & ObjPtr(Me), X + RadiusOuter, Y, vbGreen, 6)
  Set LightSourceCenter = ControlPoints.Add("LightSourceCenter" & ObjPtr(Me), X + LightSourceOffsetXY, Y + LightSourceOffsetXY, vbRed, 4)
  Set CenterDrag = ControlPoints.Add("CenterDrag" & ObjPtr(Me), X, Y, vbMagenta, 9)
End Sub

Public Function GetMaskPattern(CC As cCairoContext) As cCairoPattern

Dim Pat As cCairoPattern '<- this is *not* our MaskPattern (it's only locally used for our RadialGradient-Def)
  
  
  'At first, we clip a rectangular region on our passed "main-cc" beforehand - this is done only,
  'to save some memory - since the then following PushGroup->PopGroup codeblock results
  'in a "PixelPattern" (our Alpha-Mask) this time - and the ClipExplicit beforehand ensures,
  'that only the Pixels within the rectangular clipping-area are stored within the resulting
  'PixelMask-Patter we return here (from CC.PopGroup) ...
  'If you omit the CC.ClipExplicit(...) call, as well as the corresponding CC.ResetClip call at then
  'end of this routine, then the example will also work, but the created MaskImage will use more
  '"unused, black AlphaPixels" than necessary then
  CC.ClipExplicit CenterDrag.X - RadiusOuter, CenterDrag.Y - RadiusOuter, 2 * RadiusOuter, 2 * RadiusOuter
    
    'this is "opening-up" a new ("isolated" from CC, but based on it) Pixel-Surface after all...
    'and you can decide, if this new (under the hood) created Surface only contains the AlphaChannel
    'by specifiying the appropriate Parameter - so, as said, all the drawings which follow a CC.PushGroup
    'are not "received" within the original Surface the CC was derived from, but in an isolated, new one
    CC.PushGroup CAIRO_CONTENT_COLOR_ALPHA
      
      'now basically the same drawings here, you already know from the cShapeCircle-Class within
      'the Gradients-Demo (also the same coords are used) - I've only inverted the Gradient-Colors,
      'so that the "LightSource" is now the source of "dark-light" ;-) + the final "Stroke-Border"
      'around the circular Shape is omitted (so we have only a plain Gradient-Fill remaining here)
        
      'as always, we define the path first... (in this case a simple circle is created)
      CC.Arc CenterDrag.X, CenterDrag.Y, RadiusOuter

      Set Pat = Cairo.CreateRadialPattern(CenterDrag.X, CenterDrag.Y, RadiusOuter * 1.2, LightSourceCenter.X, LightSourceCenter.Y, 3)
        Pat.AddColorStop 1, vbBlack, 1
        Pat.AddColorStop 0, vbBlack, 0
      CC.Fill True, Pat '<- note the Optional "True"-param before "Pat", meaning we do *not* want to close the path yet...
      
      '...since we plan to draw a Border too (with a different Stroke-Color)
      CC.SetLineWidth 1
      CC.SetSourceColor &H101010, 0.1
      CC.Stroke '*now* the Path gets closed (no Optional DontClosePath-Param set) - and we ensure thereby a combined Fill+Stroke
      
    Set GetMaskPattern = CC.PopGroup '<-- this finally delivers the new Alpha-Mask-Surface, encapsulated in a new created Pattern
    
  CC.ResetClip 'as always, we need to reset Clips
End Function


Public Property Get RadiusOuter() As Double
  RadiusOuter = ROuter.X - CenterDrag.X
End Property


'Control-Point Movement-Events are received below, and we will act accordingly with the necessary adaptions

Private Sub Router_PositionChanging(NewX As Double, NewY As Double)
  NewY = CenterDrag.Y

  If NewX - CenterDrag.X < 20 Then NewX = CenterDrag.X + 20   'we restrict the outer Radius to min 20 pixels
  If NewX - CenterDrag.X > 150 Then NewX = CenterDrag.X + 150   'we restrict the outer Radius to man 150 pixels
  
  'and this ensures, that the LightSource-Center is adapted accordingly to the new Radius -> (NewX - CenterDrag.X)
  AdjustLightSourcePlacement CenterDrag.X, CenterDrag.Y, (NewX - CenterDrag.X) / RadiusOuter
End Sub
Private Sub LightSourceCenter_PositionChanging(NewX As Double, NewY As Double)
Dim NewR#
  NewR = Sqr((CenterDrag.X - NewX) ^ 2 + (CenterDrag.Y - NewY) ^ 2)
  
  If NewR > RadiusOuter * 1.1 Then
    NewX = CenterDrag.X + (NewX - CenterDrag.X) / NewR * RadiusOuter * 1.1
    NewY = CenterDrag.Y + (NewY - CenterDrag.Y) / NewR * RadiusOuter * 1.1
  End If
End Sub
Private Sub CenterDrag_PositionChanging(NewX As Double, NewY As Double)
Dim RO#
  'we need to buffer the current Radius and Coords beforehand, to not mess-up the coord-adaptions below, since both are dynamically calculated in Properties
  RO = RadiusOuter
  
  ROuter.X = NewX + RO
  ROuter.Y = NewY
  
  'again, here the LightSource-Center-adjustment, according to our new, move "Main-Center-Point"
  AdjustLightSourcePlacement NewX, NewY
End Sub

'small helper-Sub, to adjust the relative placement of the "LightSource"-CenterControlPoint
Private Sub AdjustLightSourcePlacement(CenterX As Double, CenterY As Double, Optional Ratio As Double = 1)
Dim RLdX#, RLdY#
  RLdX = LightSourceCenter.X - CenterDrag.X
  RLdY = LightSourceCenter.Y - CenterDrag.Y
  
  LightSourceCenter.X = CenterX + RLdX * Ratio
  LightSourceCenter.Y = CenterY + RLdY * Ratio
End Sub


