2

I'm trying to extend a Vector2DFieldInterpolator in order to make a smooth arc animation given three points (start, end, and a point inbetween).

I've gotten it to work by setting the key and keyValue fields on a regular Vector2DFieldInterpolator, but now I wanted to put it all into one component so it can be easily reused.

Here is a minimum viable example of what I have right now:

Example.xml:

<?xml version="1.0" encoding="utf-8" ?>
<component name="ArcInterpolatorExample" extends="Scene" >
  <script type="text/brightscript" uri="pkg:/components/Example.brs"/>
  <children>
    <Rectangle id = "animateMe" height = "20" width = "20" color="#ffff00ff" 
      translation="[250,250]"/>
    <Label id="labelA" text="A" translation="[250,250]"/>
    <Label id="labelB" text="B" translation="[125,250]"/>
    <Label id="labelC" text="C" translation="[75,180]"/>
    <Animation 
      id="animate" 
      easeFunction="linear"
      duration="10">
        <ArcInterpolator 
          id="arcInterpol" 
          start="[250,250]" 
          middle="[125,250]" 
          end="[75,180]" 
          fieldToInterp="animateMe.translation"
        />
    </Animation>
  </children>
</component>

Example.brs:

sub init()
    m.arcAnimator = m.top.findNode("animate")
    m.arcAnimator.control = "start"
end sub

ArcInterpolator.xml

<?xml version="1.0" encoding="utf-8" ?>
<component name="ArcInterpolator" extends="Vector2DFieldInterpolator">
    <interface>
        <field id="start" type="vector2d" onChange="onCoordinateSet"/>
        <field id="middle" type="vector2d" onChange="onCoordinateSet"/>
        <field id="end" type="vector2d" onChange="onCoordinateSet"/>
    </interface>
    <script type="text/brightscript" uri="pkg:/components/ArcInterpolator.brs"/>
</component>

ArcInterpolator.brs

sub init()
  m.pi = 3.1415927
end sub

sub onCoordinateSet()
    ' check that no two points are the same
    if (m.top.start[0] = m.top.middle[0] and m.top.start[1] = m.top.middle[1]) or (m.top.start[0] = m.top.end[0] and m.top.start[1] = m.top.end[1]) or (m.top.middle[0] = m.top.end[0] and m.top.middle[1] = m.top.end[1]) then
        return
    else
        setValues()
    end if
end sub

sub setValues()
    startpoint = m.top.start
    midpoint = m.top.middle
    endpoint = m.top.end
    numOfPoints = 11

    dim keys[numOfPoints-1]
    dim values[numOfPoints-1]
    keys[0] = 0.0
    keys[numOfPoints-1] = 1.0
    values[0] = startpoint
    values[numOfPoints-1] = endpoint

    ' a bunch of calculation is done here and keys and values are set

    m.top.key = keys
    m.top.keyValue = values
end sub
MMAdams
  • 1,508
  • 15
  • 29

1 Answers1

2

It turns out that YES, it is possible to extend an interpolator but that I was going about this the wrong way. There's a field on the interpolator called fraction which is updated during the course of the animation. There is also a field called fieldToInterp which describes the node and field to change.

In order to write your own interpolator, you need to find the node and field described by fieldToInterp and store it in m and then observe fraction and update that node's field according to what the fraction is. Here's what this ends up looking like:

ArcInterpolator.brs:

sub init()
  m.pi = 3.1415927
  m.top.observeField("fraction", "calculateValue")
  m.top.observeField("fieldToInterp", "findNodeToMove")
end sub

sub findNodeToMove(event as object)
    nodeAndField = event.getData()
    if right(nodeAndField, 12) <> ".translation" then return
    length = len(nodeAndField)
    nodeName = left(nodeAndField, length - 12)

    currentNode = m.top
    while currentNode.getParent() <> invalid
        currentNode = currentNode.getParent()
        node = currentNode.findNode(nodeName)
        if node <> invalid
            m.nodeToMove = node
            exit while
        end if
    end while
end sub

sub onCoordinateSet()
    ' check that no two points are the same
    if (m.top.start[0] = m.top.middle[0] and m.top.start[1] = m.top.middle[1]) or (m.top.start[0] = m.top.end[0] and m.top.start[1] = m.top.end[1]) or (m.top.middle[0] = m.top.end[0] and m.top.middle[1] = m.top.end[1]) then
        return
    else
        setValues()
    end if
end sub

sub setValues()
    ' do some math to set the center of the circle, total angle, start angle etc.
end sub

sub calculateValue(event as object)    
    fraction = event.getData()
    angle = fraction * m.totalAngle + m.startAngle
    dim position[1]
    position[0] = m.center[0] + m.radius * cos(angle)
    position[1] = m.center[1] + m.radius * sin(angle)
    m.nodeToMove.translation = position
end sub
MMAdams
  • 1,508
  • 15
  • 29