Android Code Snippet [LibGDX CatMullRomSpline] Constant Speed Progress Calculator

wonder

Expert
Licensed User
Hi!

If you ever encounter the problem described on the link below, you can use this class to solve this problem:
https://stackoverflow.com/questions/32665262/libgdx-path-catmullromspline-constant-speed

Class Module: ConstSpeedProgressCalculator
B4X:
Private Sub Class_Globals
    Private ref                 As Reflector
    Private derivative_average  As Float
    Private path_completion     As Float
    Private derivative          As lgMathVector2
End Sub

Public Sub Initialize(spline As lgMathCatmullRomSpline, samples As Int)
    ref.Target = spline.InternalObject
    Dim out As lgMathVector2
    Dim f = 0 As Float
    Do While f < 1
        out = DerivativeAt(out, f)
        derivative_average = derivative_average + out.len
        f = f + (1 / samples)
    Loop
    derivative_average = derivative_average / samples
End Sub

Public Sub Update(speed As Float, deltatime As Float, reset_automatically As Boolean)
    derivative = DerivativeAt(derivative, path_completion)
    path_completion = path_completion + (derivative_average * speed * deltatime / derivative.len)
    If reset_automatically Then path_completion = path_completion Mod 1
End Sub

Public Sub getPathCompletion As Float
    Return path_completion
End Sub

Public Sub Reset
    path_completion = 0
End Sub

Private Sub DerivativeAt(out As lgMathVector2, completion As Float) As lgMathVector2
    Return ref.RunMethod4("derivativeAt", Array(out, completion), Array As String("java.lang.Object", "java.lang.float"))
End Sub

Usage:

1. Declare the ProgressCalculator instance as a global (recommended):
B4X:
Sub Process_Globals
    ...
    Private ProgressCalculator As ConstSpeedProgressCalculator
    ...
End Sub

2. Initialize the class right after you initialize the spline.
B4X:
ProgressCalculator.Initialize(mySpline, 100) 'more samples = smoother result

3. Replace, on your update method (or whatever it's named), the usual:
B4X:
progress = progress + (lGdx.graphics.DeltaTime * speed) 'Value between 0 and 1
with:
B4X:
ProgressCalculator.Update(speed, lGdx.graphics.DeltaTime, False) 'or True
progress = ProgressCalculator.PathCompletion 'Value between 0 and 1

B4X:
...

Sub InitializeSpline(s As lgMathCatmullRomSpline) As Boolean
    If nodes.Length < 4 Then Return False
    s.Initialize(Utils.MathVec2Array(nodes), False)
    Return True
End Sub

Sub InitializePathPreview As Boolean
    preview_timestamp = 0
    preview_completion = 0
    preview_path_complete = False
    active_path = Paths.get(SelectedPath.Number)
    nodes = Utils.ExtrapolatedPath(active_path.nodes)
    Dim result = InitializeSpline(preview_spline) As Boolean
    If result Then preview_progress_calc.Initialize(preview_spline, 100)
    Return result
End Sub

Sub DrawPathPreview(shape_renderer As lgShapeRenderer, viewport As Rect)
    If SelectedPath.IsUpdated(True) Then InitializePathPreview
 
    If Not(active_path.IsInitialized)          _
    Or Not(active_path.Settings.IsInitialized) _
    Or active_path.Settings.Size == 0          _
    Or SelectedPath.Number < 0                 _
    Or nodes.Length < 4 Then Return            _
    '-------------------------------------------
 
    Dim speed = Utils.ToNumber(active_path.Settings.Get          ("speed"        )) * 0.5  As Double
    Dim itrpl = Maps.Interpolation.Get(active_path.Settings.Get  ("interpolation"))        As lgMathInterpolation
 
    If preview_path_complete Then
        preview_timestamp = 0
        preview_completion = 0
        preview_progress_calc.Reset
        preview_path_complete = False
    End If
 
    Dim delay = 300 * 1 / speed As Int
    Dim shape_size As Double
    Dim multiplier As Double
    Dim min_shape_size = Constants.ENDNODE_RADIUS / 3 As Double
    If preview_completion >= 1 Then
        ApplyPreviewPathDelay(delay)
        multiplier = Min(delay, DateTime.Now - preview_timestamp) / delay
        shape_size = min_shape_size + ((Constants.ENDNODE_RADIUS - min_shape_size) * multiplier)
        preview_spline_motion.Set(active_path.LastNode.x, active_path.LastNode.y)
    Else
        multiplier = 0
        shape_size = min_shape_size
        preview_progress_calc.Update(speed, volume.LibGDX.Graphics.DeltaTime, False)
        preview_completion = preview_progress_calc.PathCompletion
        preview_spline.ValueAt(preview_spline_motion, itrpl.apply(preview_completion))
    End If
 
    shape_renderer.Begin(shape_renderer.SHAPETYPE_Filled)
        shape_renderer.SetColorRGBA(1.0, 0.0, 0.0, 1.0 * (1 - multiplier))
        shape_renderer.Circle(                          _
            viewport.CenterX + preview_spline_motion.x, _
            viewport.CenterY + preview_spline_motion.y, _
            shape_size                                  _
        )
    shape_renderer.End
End Sub

Sub ApplyPreviewPathDelay(delay As Int)
    If preview_timestamp == 0 Then
        preview_timestamp = DateTime.now
    else if DateTime.Now - preview_timestamp >= delay Then
        preview_timestamp = 0
        preview_path_complete = True
    End If
End Sub

...
 
Last edited:
Top