Attribute VB_Name = "ModSpline"
' ModSpline.bas

' Adapted from code at VB-Helper.com

' http://www.vb-helper.com/howto_smooth_curve.html

Option Explicit

Public Type PointF
   x As Single
   y As Single
End Type

' Draw a cardinal spline built from connected Bezier curves.
Public Sub DrawCurve(ByVal dt As Single, _
          ByVal Tension As Single, pts() As PointF, RR As Byte, GG As Byte, BB As Byte)
Dim control_scale As Single
Dim pt As PointF
Dim pt_before As PointF
Dim pt_after As PointF
Dim pt_after2 As PointF
Dim Di As PointF
Dim DiPlus1 As PointF
Dim p1 As PointF
Dim p2 As PointF
Dim p3 As PointF
Dim p4 As PointF
Dim i As Long

    control_scale = CSng(Tension / 0.5 * 0.175)
    For i = LBound(pts) To UBound(pts) - 1
        pt_before = pts(Max(i - 1, 0))
        pt = pts(i)
        pt_after = pts(i + 1)
        pt_after2 = pts(Min(i + 2, UBound(pts)))

        p1 = pts(i)
        p4 = pts(i + 1)

        Di.x = pt_after.x - pt_before.x
        Di.y = pt_after.y - pt_before.y
        p2.x = pt.x + control_scale * Di.x
        p2.y = pt.y + control_scale * Di.y

        DiPlus1.x = pt_after2.x - pts(i).x
        DiPlus1.y = pt_after2.y - pts(i).y
        p3.x = pt_after.x - control_scale * DiPlus1.x
        p3.y = pt_after.y - control_scale * DiPlus1.y

        DrawBezier dt, p1, p2, p3, p4, RR, GG, BB
    Next i
End Sub
' Return the minimum of the parameters.
Private Function Min(ParamArray values() As Variant)
Dim i As Long
Dim result As Single

    result = values(LBound(values))
    For i = LBound(values) + 1 To UBound(values)
        If result > values(i) Then result = values(i)
    Next i

    Min = result
End Function
' Return the maximum of the parameters.
Private Function Max(ParamArray values() As Variant)
Dim i As Long
Dim result As Single

    result = values(LBound(values))
    For i = LBound(values) + 1 To UBound(values)
        If result < values(i) Then result = values(i)
    Next i

    Max = result
End Function

' Parametric X function for drawing a degree 3 Bezier curve.
Private Function x(ByVal t As Single, ByVal x0 As Single, ByVal x1 As Single, ByVal x2 As Single, ByVal x3 As Single) As Single
    x = CSng( _
        x0 * (1 - t) ^ 3 + _
        x1 * 3 * t * (1 - t) ^ 2 + _
        x2 * 3 * t ^ 2 * (1 - t) + _
        x3 * t ^ 3 _
        )
End Function
' Parametric Y function for drawing a degree 3 Bezier curve.
Private Function y(ByVal t As Single, ByVal y0 As Single, ByVal y1 As Single, ByVal y2 As Single, ByVal y3 As Single) As Single
    y = CSng( _
        y0 * (1 - t) ^ 3 + _
        y1 * 3 * t * (1 - t) ^ 2 + _
        y2 * 3 * t ^ 2 * (1 - t) + _
        y3 * t ^ 3 _
        )
End Function

' Draw the Bezier curve.
Public Sub DrawBezier(ByVal dt As Single, _
      pt0 As PointF, pt1 As PointF, pt2 As PointF, pt3 As PointF, RR As Byte, GG As Byte, BB As Byte)
    ' Draw the curve.
    Dim t As Single
    Dim x0 As Single
    Dim y0 As Single
    Dim x1 As Single
    Dim y1 As Single

    t = 0#
    x1 = x(t, pt0.x, pt1.x, pt2.x, pt3.x)
    y1 = y(t, pt0.y, pt1.y, pt2.y, pt3.y)
    t = t + dt
    Do While t < 1#
        x0 = x1
        y0 = y1
        x1 = x(t, pt0.x, pt1.x, pt2.x, pt3.x)
        y1 = y(t, pt0.y, pt1.y, pt2.y, pt3.y)
        'pic.Line (x0, y0)-(x1, y1)
        If aSelectAccepted Then
           BresLineCALC x0, y0, x1, y1, RR, GG, BB
        Else
           BresLine x0, y0, x1, y1, RR, GG, BB
        End If
        t = t + dt
    Loop

    ' Connect to the final point.
    t = 1#
    x0 = x1
    y0 = y1
    x1 = x(t, pt0.x, pt1.x, pt2.x, pt3.x)
    y1 = y(t, pt0.y, pt1.y, pt2.y, pt3.y)
    'pic.Line (x0, y0)-(x1, y1)
    If aSelectAccepted Then
       BresLineCALC x0, y0, x1, y1, RR, GG, BB
    Else
       BresLine x0, y0, x1, y1, RR, GG, BB
    End If
End Sub
