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

Public ImageKey As String

Private WithEvents CenterDrag As cControlPoint
Attribute CenterDrag.VB_VarHelpID = -1
Private WithEvents PRotateResize0 As cControlPoint
Attribute PRotateResize0.VB_VarHelpID = -1
Private WithEvents PRotateResize1 As cControlPoint
Attribute PRotateResize1.VB_VarHelpID = -1
Private WithEvents PRotateResize2 As cControlPoint
Attribute PRotateResize2.VB_VarHelpID = -1
Private WithEvents PRotateResize3 As cControlPoint
Attribute PRotateResize3.VB_VarHelpID = -1

Private ImageDiagonalWidth As Double, CurrentAlphaDelta As Double

Friend Sub Init(ControlPoints As cControlPoints, ImageKey As String, ByVal X As Double, ByVal Y As Double, ByVal Radius As Double, ByVal Angle As Double)
Dim DiagonalRatio As Double, NewWidthHalf As Double, NewHeightHalf As Double
  
  Me.ImageKey = ImageKey 'we store the ImageKey of the Image we are responsible to handle inside here...
  
  Set CenterDrag = ControlPoints.Add(ImageKey, X, Y, vbMagenta, 60, 0.06) '...create our CenterDrag-ControlPoint...

  With Cairo.ImageList(ImageKey) '...do some pre-calculations, according to the Image-Size and the desired Radius we've got as a Param...
    ImageDiagonalWidth = Sqr(.Width ^ 2 + .Height ^ 2)
    DiagonalRatio = 2 * Radius / ImageDiagonalWidth
    NewWidthHalf = DiagonalRatio * .Width / 2
    NewHeightHalf = DiagonalRatio * .Height / 2
  End With
  
  '...and use these precalculated Values from above, to create and move our 4 "Outer-ControlPoints" (ignoring the Angle-Param yet)
  Set PRotateResize0 = ControlPoints.Add(ImageKey & "_0", X - NewWidthHalf, Y - NewHeightHalf, vbGreen)
  Set PRotateResize1 = ControlPoints.Add(ImageKey & "_1", X + NewWidthHalf, Y - NewHeightHalf, vbGreen)
  Set PRotateResize2 = ControlPoints.Add(ImageKey & "_2", X + NewWidthHalf, Y + NewHeightHalf, vbGreen)
  Set PRotateResize3 = ControlPoints.Add(ImageKey & "_3", X - NewWidthHalf, Y + NewHeightHalf, vbGreen)
  
  'finally we make use of a Helper-Function in this Code-Module, to rotate our Outer-Controlpoints using the passed Angle-Param
  AdjustDependentControlPoints 1, Angle
End Sub

Public Sub Draw(CC As cCairoContext)
Dim Srf As cCairoSurface, CurImageAngle#, CurDiagonalWidth#, ScaleRatio#, NewDX#, NewDY#
  
  'we can retrieve the current Image-Angle easily from our Top-Controlpoints P0 and P1
  CurImageAngle = Cairo.CalcArc(PRotateResize1.Y - PRotateResize0.Y, PRotateResize1.X - PRotateResize0.X)
  
  'and the current DiagonalWidth is easy too (over P0 and P2)
  CurDiagonalWidth = Sqr((PRotateResize0.Y - PRotateResize2.Y) ^ 2 + (PRotateResize0.X - PRotateResize2.X) ^ 2)
  
  ScaleRatio = CurDiagonalWidth / ImageDiagonalWidth
  
  Set Srf = Cairo.ImageList(ImageKey) 'let's get our current Surface from our internal ImageKey from the Cairo.Imagelist first

  'Now we have everything, to draw finally from the above Srf
  '(respecting the current Translate, Rotate and Scale of course, which we've calculated from our ControlPoint-Coords)
  CC.Save
    
    NewDX = ScaleRatio * Srf.Width
    NewDY = ScaleRatio * Srf.Height
    
    'the follwing two lines ensure the rotation around our current Center-Point
    CC.TranslateDrawings CenterDrag.X, CenterDrag.Y
    CC.RotateDrawings CurImageAngle
    
    'and finally we do the scaled rendering here
    CC.RenderSurfaceContent Srf, -NewDX / 2, -NewDY / 2, NewDX, NewDY, CAIRO_FILTER_GOOD, 1 - CurrentAlphaDelta
    
    CC.RoundedRect -NewDX / 2 - 1, -NewDY / 2 - 1, NewDX + 2, NewDY + 2, 3 + 15 * ScaleRatio
      CC.SetLineWidth 1 + 15 * ScaleRatio
      CC.SetSourceColor &H303030
    CC.Stroke
    
  CC.Restore
End Sub

'------------- reactions to drag-interaction on the center-point ----------------
Private Sub CenterDrag_PositionChanging(NewX As Double, NewY As Double)
  DeltaMoveControlPoint PRotateResize0, NewX - CenterDrag.X, NewY - CenterDrag.Y
  DeltaMoveControlPoint PRotateResize1, NewX - CenterDrag.X, NewY - CenterDrag.Y
  DeltaMoveControlPoint PRotateResize2, NewX - CenterDrag.X, NewY - CenterDrag.Y
  DeltaMoveControlPoint PRotateResize3, NewX - CenterDrag.X, NewY - CenterDrag.Y

  HandleMouseDownState CenterDrag
End Sub

Private Sub DeltaMoveControlPoint(P As cControlPoint, ByVal dX As Double, ByVal dY As Double)
  P.X = P.X + dX
  P.Y = P.Y + dY
End Sub
'------------- end of reactions to drag-interaction on the center-point ----------------


'all the rest of the Helper-Subs below is related to reactions on dragging of the outer resize-points
'...
'starting with the four Control-Point-Event-Subs
Private Sub PRotateResize0_PositionChanging(NewX As Double, NewY As Double)
  ReactToOuterPointChange PRotateResize0, NewX, NewY
End Sub
Private Sub PRotateResize1_PositionChanging(NewX As Double, NewY As Double)
  ReactToOuterPointChange PRotateResize1, NewX, NewY
End Sub
Private Sub PRotateResize2_PositionChanging(NewX As Double, NewY As Double)
  ReactToOuterPointChange PRotateResize2, NewX, NewY
End Sub
Private Sub PRotateResize3_PositionChanging(NewX As Double, NewY As Double)
  ReactToOuterPointChange PRotateResize3, NewX, NewY
End Sub

Private Sub ReactToOuterPointChange(P As cControlPoint, NewX As Double, NewY As Double)
Dim dXNew As Double, dYNew As Double, NewRadius As Double, NewAngle As Double
Dim dXOld As Double, dYOld As Double, OldRadius As Double, OldAngle As Double
  dXNew = NewX - CenterDrag.X
  dYNew = NewY - CenterDrag.Y
  NewRadius = Sqr(dXNew ^ 2 + dYNew ^ 2)
  NewAngle = Cairo.CalcArc(dYNew, dXNew)

  dXOld = P.X - CenterDrag.X
  dYOld = P.Y - CenterDrag.Y
  OldRadius = Sqr(dXOld ^ 2 + dYOld ^ 2)
  OldAngle = Cairo.CalcArc(dYOld, dXOld)
  
  AdjustDependentControlPoints NewRadius / OldRadius, NewAngle - OldAngle
  
  HandleMouseDownState P
End Sub

Private Sub AdjustDependentControlPoints(ByVal RadiusScale As Double, ByVal NewAngleDelta As Double)
Dim MRot As cCairoMatrix

  'here we use a Cairo-Matrix again, to ease our Vector-Calculations with regards to Scale and Rotate
  'the 3 prepare-steps below ensure, that MRot is set-up with a new Coord-System, which the
  'old X-Y pairs of our 4 Outer-ControlPoints will then use for their transformations
  '(done with a single "Matrix.CalculatePoint"-call in the subroutine: RotateAndScaleCoordsOnControlPoint
  Set MRot = Cairo.CreateIdentityMatrix  'create a new Identity Matrix
  MRot.ScaleCoords RadiusScale, RadiusScale 'and prepare it again with the delta to our old scale-value
  MRot.RotateCoords NewAngleDelta  'as well as with the delta to our old angle
  
  RotateAndScaleCoordsOnControlPoint PRotateResize0, MRot
  RotateAndScaleCoordsOnControlPoint PRotateResize1, MRot
  RotateAndScaleCoordsOnControlPoint PRotateResize2, MRot
  RotateAndScaleCoordsOnControlPoint PRotateResize3, MRot
End Sub

Private Sub RotateAndScaleCoordsOnControlPoint(P As cControlPoint, MRot As cCairoMatrix)
Dim X As Double, Y As Double
  X = P.X - CenterDrag.X: Y = P.Y - CenterDrag.Y 'we need to subtract a new difference vector (its x,y coords) beforehand, which is relative to our center-point
  
  MRot.CalculatePoint X, Y 'the Cairo-Matrix does all the affine Transformations for us in a convenient way (in a single call)
  
  P.X = CenterDrag.X + X: P.Y = CenterDrag.Y + Y 'the now transformed X/Y-pair of the difference-vector is now added again to the center-point and the Sum-Coords stored back in P
End Sub

Private Sub HandleMouseDownState(P As cControlPoint)
  If Not P.MDown Then CurrentAlphaDelta = 0: Exit Sub 'we do nothing special in this case, just reset the AlphaDelta and leave
  
  If CurrentAlphaDelta = 0 Then 'only in case P is in MouseDown-State, we do a bit more, but only once (if the AlphaDelta is yet 0)
    fCairoDemo.MoveToFront Me
    CurrentAlphaDelta = 0.3
  End If
End Sub
