LDraw.org Discussion Forums
LDCad help combining animation movements - Printable Version

+- LDraw.org Discussion Forums (https://forums.ldraw.org)
+-- Forum: LDraw Programs (https://forums.ldraw.org/forum-7.html)
+--- Forum: LDraw Editors and Viewers (https://forums.ldraw.org/forum-11.html)
+--- Thread: LDCad help combining animation movements (/thread-23814.html)



LDCad help combining animation movements - Walt White - 2020-01-02

I made a simple mechanism demonstrating some Technic capabilities.  I'd like to use it in a workshop at a Bricks By The Bay convention next summer.

Animations make good advertisements for showing people what Technic can do.  But I'm having a problem combining three movements into a single movement.   It's awkward to describe with just words, so I made a YouTube video showing the problem.

The LDraw file I will use for building instructions is here.

The LDraw file I used for the animation is here.  It organizes sub-models by their connectivity, not by how you would assembly the MOC.

The three Lua code files are here, here, and here.

Any help is appreciated.

Walt


RE: LDCad help combining animation movements - Roland Melkert - 2020-01-02

(2020-01-02, 20:39)Walt White Wrote: But I'm having a problem combining three movements into a single movement.
Having all three movements in the same animation presents a major problem (or shortcoming) of the animation scripting engine.

This because it calculates the state of ANY frame based on the current frame time.

This is usually done by a single control angle from which all others derive their state, but with this setup you have 3 different paths. To solve it you need to 'cache' the starting situation of every point in the animation where you switch from one to the other mechanical mode.

I had the same problem in the 8860 model concerning its gear box stuff.

As a result I wrote some helper 'classes' to make it a lot easier to handle.

To use those tools you need to switch the whole animation to the 'story telling' way of doing things though.

Below is a basic setup incorporating the continuous part of your animation.

Combined with the comments in the 8860 animation you might be able to add the rest yourself.

But if needed I could write something more complete this weekend.


Code:
genTools=require('genTools')
aniTools=require('aniTools')

function mainActor()

  local mainSf=ldc.subfile()
  local result=aniTools.actor()

  --Joints and aniElms
  result.inputGearsAngle=result:addAniElm(aniTools.aniElm(0))
  result:addJoint(aniTools.angleJoint(mainSf:getRef('InputGears.ldr'), result.inputGearsAngle, ldc.vector(0, 1, 0)))

  result.knobPairAngle=result:addAniElm(aniTools.aniElm(0))
  result:addJoint(aniTools.angleJoint(mainSf:getRef('KnobPair.ldr'), result.knobPairAngle, ldc.vector(0,0, 1)))

  result.endGearsAngle=result:addAniElm(aniTools.aniElm(0))
  result:addJoint(aniTools.angleJoint(mainSf:getRef('EndGears.ldr'), result.endGearsAngle, ldc.vector(0,1,0)))

  result.pivotDriverAngle=result:addAniElm(aniTools.aniElm(0))
  result:addJoint(aniTools.angleJoint(mainSf:getRef('PivotDriver.ldr'), result.pivotDriverAngle, ldc.vector(0,1,0)))


  return result
end

function onStart()

  story=aniTools.story()
  local actor=story:addActor(mainActor())
  local seq=story:addSequence(aniTools.sequence())

  --actions
  seq:addAction(aniTools.speedAction(actor.inputGearsAngle, 0, nil, 45))
  seq:addAction(aniTools.ratioDepAction(actor.knobPairAngle, 0, nil, actor.inputGearsAngle, -1))
  seq:addAction(aniTools.ratioDepAction(actor.endGearsAngle, 0, nil, actor.knobPairAngle, -1))
  seq:addAction(aniTools.ratioDepAction(actor.pivotDriverAngle, 0, nil, actor.endGearsAngle, -1))


  --This helps the story table optimize some things it would otherwise do in the first onFrame apply call.
  -- Keep it at the bottom of the onStart function.
  story:prep()
end

function onFrame()
  story:apply(ldc.animation():getFrameTime())
end

function register()

  local ani=ldc.animation('My animation')
  ani:setEvent('start', 'onStart')
  ani:setEvent('frame', 'onFrame')
end

register()



RE: LDCad help combining animation movements - Walt White - 2020-01-02

Roland,

Appreciate the help.  I'll try to figure out the 8860 code by looking at the "actors" and hopefully my example will have only three actors and each will have a simple motion to describe.

The trick for me will be to recognize the main "story" components out of the 1100 lines of code in 8860.

Good mental exercise!  Keep me busy this weekend.

Walt


RE: LDCad help combining animation movements - Roland Melkert - 2020-01-03

(2020-01-02, 23:37)Walt White Wrote: Appreciate the help.  I'll try to figure out the 8860 code by looking at the "actors" and hopefully my example will have only three actors and each will have a simple motion to describe.

The trick for me will be to recognize the main "story" components out of the 1100 lines of code in 8860.

Both 8860(and your model have only one actor. Actors are used to describe the bone structure of the things moving. This way you won't have to worry about recursion of stuff attached to other stuff.

As it has been a couple of years for me using this stuff myself I went and made the mechanical part of the animation (as far I understand it).

It needs a better story (not just infinite rotation at the starting gear)

But it should get you going a whole lot faster as there is next to nothing documentation wise Big Grin

Code:
genTools=require('genTools')
aniTools=require('aniTools')

function mainActor()

  local mainSf=ldc.subfile()
  local result=aniTools.actor()

  --Joints and aniElms
  result.inputGearsAngle=result:addAniElm(aniTools.aniElm(0))
  result:addJoint(aniTools.angleJoint(mainSf:getRef('InputGears.ldr'), result.inputGearsAngle, ldc.vector(0,1,0)))

  result.knobPairAngle=result:addAniElm(aniTools.aniElm(0))
  result:addJoint(aniTools.angleJoint(mainSf:getRef('KnobPair.ldr'), result.knobPairAngle, ldc.vector(0,0,1)))

  result.endGearsAngle=result:addAniElm(aniTools.aniElm(0))
  result:addJoint(aniTools.angleJoint(mainSf:getRef('EndGears.ldr'), result.endGearsAngle, ldc.vector(0,1,0)))

  result.pivotDriverAngle=result:addAniElm(aniTools.aniElm(0))
  result:addJoint(aniTools.angleJoint(mainSf:getRef('PivotDriver.ldr'), result.pivotDriverAngle, ldc.vector(0,1,0)))

  result.pivotArmAngle=result:addAniElm(aniTools.aniElm(0))
  local arm=result:addJoint(aniTools.angleJoint(mainSf:getRef('PivotArm.ldr'), result.pivotArmAngle, ldc.vector(0,1,0)))

  --Attach the hor arm axle to the arm joint instead of the actor itself.
  result.pivotHorizontalsAngle=result:addAniElm(aniTools.aniElm(0))
  arm:addJoint(aniTools.angleJoint(mainSf:getRef('PivotHorizontals.ldr'), result.pivotHorizontalsAngle, ldc.vector(0,0,1)))

  --Create a state tracking table for the pivot angle as everyhing behind it depends on the current mode.
  result.modeStateInfo=result:addStateInfo(aniTools.numberDepStateInfo(result.pivotDriverAngle, {mode=0}))

  return result
end

function modeDepAction(actor)

  --Custom state dependend action, to handle all possible dependency chains based on a current mode variable.

  local result=aniTools.stateInfoDepAction(actor.modeStateInfo, 0, nil)
  result.actor=actor
 
  result.initTotals=function(self)
    self.totPivotArmAngle=0
    self.totPivotHorizontalsAngle=0
  end
 
  result.processState=function(self, state)
 
    local mode=state.value.mode
    local mainAngle=state.elmValueDiff --don't access pivotDriverAngle directly as that gives it's current ABS angle. We want the one relative to the start of this state segment.

    local armAngle=0
    local horAngle=0   

    if mode==0 then
      --only arm moves
      armAngle=mainAngle
    elseif mode==1 then
      --block arm
      horAngle=-mainAngle
    else
      --block hor
      armAngle=mainAngle
      horAngle=-armAngle
    end
   
    self.totPivotArmAngle=self.totPivotArmAngle+armAngle
    self.totPivotHorizontalsAngle=self.totPivotHorizontalsAngle+horAngle
  end
 
  result.applyTotals=function(self)
    self.actor.pivotArmAngle:setValue(self.totPivotArmAngle)
    self.actor.pivotHorizontalsAngle:setValue(self.totPivotHorizontalsAngle)
  end
 
  return result
end

function onStart()

  story=aniTools.story()
  local actor=story:addActor(mainActor())
  local seq=story:addSequence(aniTools.sequence())
 
  --==actions==--
 
  --base gears run forever
  seq:addAction(aniTools.speedAction(actor.inputGearsAngle, 0, nil, 45))
  seq:addAction(aniTools.ratioDepAction(actor.knobPairAngle, 0, nil, actor.inputGearsAngle, -1))
  seq:addAction(aniTools.ratioDepAction(actor.endGearsAngle, 0, nil, actor.knobPairAngle, -1))
  seq:addAction(aniTools.ratioDepAction(actor.pivotDriverAngle, 0, nil, actor.endGearsAngle, -1))
 
  --Setup state dep actions
  seq:addAction(modeDepAction(actor))  --custom action
  seq:addAction(aniTools.stateChangeAction(actor.modeStateInfo, 2, {mode=1}))
  seq:addAction(aniTools.stateChangeAction(actor.modeStateInfo, 4, {mode=2}))

  --==All done==--
  story:prep()
end

function onFrame()
  story:apply(ldc.animation():getFrameTime())
end

function register()

  local ani=ldc.animation('My animation')
  ani:setLength(10)
  ani:setEvent('start', 'onStart')
  ani:setEvent('frame', 'onFrame')
end

register()

Let me know if you need more comments.