Transition

Function

The Transition function is an editing node to mix or cut two clips together. It is unique in that it is really a shell to drive other functions that determine the mixing. Modifying it also modifies the timing of the second input.

The mixers can cut, which simply cuts to the second clip at the frame that it starts, or they can mix the second clip in over a specified frame range. The duration of the mix is determined by the overlap value, and starts at the frame that the second clip appears. If you modify the timing of the second clip, the overlap value changes as well.

In the following example, there are two clips.

A Transition node appended to both clips offsets the second input clip.

You can now append one effect to both clips by attaching it to the Transition node. So, how is this different from just compositing the clips with an Over node and offsetting the second clip in time? It’s different because you can easily dial in the overlap value to determine how many frames they overlap. Here, the overlap value is increased in the Transition node, and the second node shifts to the left as it increases. You can also shift the second clip, and read the overlap value in the Transition node.

On cut mode, the cut point occurs at the beginning of the second clip, not at the end of the first clip.

A common third parameter, mixPercent, is available in all mixers with the exception of “cut.” mixPercent determines the timing for the mixing. For example, for dissolve, if mixPercent is at 20, the second image is 20 percent mixed in and the first image is 80 percent. You can tune a curve interactively in the interface to adjust timing.

You can create your own custom mixers in a startup .h file. You must do two things:

Here is an example from the include/nreal.h file for horizontalWipe:

image HWipe(
  image i1=0,
  image i2=0,
  float blur=0,
  int reverse=0,
  float mixPercent="HermiteV(x,1,[0,50,50]@0,[100,50,50]@100)"
)
{
    Color1 = Color(
      max(i1.width,i2.width), 
max(i1.height,i2.height), 1, 1, red, red, 1, 0); Crop1 = Crop(Color1, 0, 0, width, height); Pan1 = Pan(Crop1, mixPercent*width/100*(reverse?-1:1)); BlurMe = Blur(Pan1,blur,0,0); IMult1 = IMult(i1, BlurMe , 1, 100, 0); Invert1 = Invert(BlurMe , "rgba"); IMult2 = IMult(Invert1, i2, 1, 100, 0); IAdd1 = IAdd(IMult1, IMult2, 1, 100); return IAdd1; } nfxDefMixer("horizontalWipe","HWipe()");

Notice how mixPercent has the default curve going from 0,0 to 100,100 for mixPercent. Also, notice how the Color generator compares the two input resolutions to determine how large to be using the max function.

Roll Your Own

Here is how to make your own transition mixer. We will choose to do a radial wipe by scaling the radius of a RGrad. I begin with a simple tree feeding two FileIns into a KeyMix. Notice I have named the two clips i1 and i2 to help me later on:

Here is the effect I want, which can be achieved by increasing the radius up and down.

I copy it in the Node View with Ctrl+C and open a text file in my $HOME/nreal/include/startup directory, pasting it in:

RGrad1 = RGrad(720, 486, 1, width/2, height/2, 1, min(width,height)/4, 
    min(width,height)/4, 0.5, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0);
in1 = FileIn("myclip.1-39#.iff", 
    "Auto", 0, 0);
in2 = FileIn("myotherclip.1-39#.iff", 
    "Auto", 0, 0);
KeyMix1 = KeyMix(in1, in2, RGrad1, 1, "A", 100, 0);


// User Interface settings

SetKey(
    "nodeView.KeyMix1.x", "156.75",
    "nodeView.KeyMix1.y", "127",
    "nodeView.RGrad1.x", "317.4916",
    "nodeView.RGrad1.y", "198.6512",
    "nodeView.in1.x", "67",
    "nodeView.in1.y", "201.125",
    "nodeView.in2.x", "202",
    "nodeView.in2.y", "198.3111"
);

I can now prune a lot of the data, keeping only that which I've made bold in the above bit, and format it as a macro. I also know I want to add the standard parameters of blur, mixPercent and reverse. I copy them from the nreal.h file's HWipe node (at the end of the file). Finally, I calculate the resolution of the RGrad by comparing the two input sizes. I've made the new bits bold in the example below:

image RadialWipe(
  image in1=0,
  image in2=0,
  float blur=0,
  int reverse=0,
  float mixPercent="HermiteV(x,1,[0,50,50]@0,[100,50,50]@100)"
)
{
  RGrad1 = RGrad(
   max(in1.width,in2.width), 
   max(in1.height,in2.height), 
   1, width/2, height/2, 1, 
   min(width,height)/4,       //This is the radius
   min(width,height)/4,       //This is the falloff
   0.5, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0);
  return KeyMix(in1, in2, RGrad1, 1, "A", 100, 0);
}

The maximum distance I have to to expand the RGrad can be calculated by measuring the distance from the center to a corner, which I can do with the distance() function ("Hey! Where'd he pull that one from?" - go to Functions By Class - Expressions). Once I've calculated this, I multiply it by the mixPercent. I also plug the blur value into the falloff parameter, with a check on the radius to see if falloff should equal 0 when radius equals 0. I also add the command to load it as a mixer in the Transition node:

image RadialWipe(
  image in1=0,
  image in2=0,
  float blur=0,
  int reverse=0,
  float mixPercent="HermiteV(x,1,[0,50,50]@0,[100,50,50]@100)"
)
{
  RGrad1 = RGrad( 
   max(in1.width,in2.width), 
   max(in1.height,in2.height), 
   1, width/2, height/2, 1, 
   mixPercent*distance(0,0,width/2,height/2)/100,      
   radius==0?0:blur,
   0.5, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0);
  return KeyMix(in1, in2, RGrad1, 1, "A", 100, 0);
}

nfxDefMixer("radialWipe", "RadialWipe()");

Now the tricky bit - reversing the mix. You might think multiplying by -1 would invert the transformation, but you'd be sadly wrong and given a last cigarette, blindfolded and shot. Instead, you often have to subtract the value from the maximum value that you expect, in the case the distance from the center to the corner. This is part of a conditional statement that tests to see if reverse is activated. I also invert the mask in the KeyMix to help it out.

image RadialWipe(
  image in1=0,
  image in2=0,
  float blur=0,
  int reverse=0,
  float mixPercent="HermiteV(x,1,[0,50,50]@0,[100,50,50]@100)"
)
{
  RGrad1 = RGrad( 
   max(in1.width,in2.width), 
   max(in1.height,in2.height), 
   1, width/2, height/2, 1, 
   reverse?distance(0,0,width/2,height/2)-
     mixPercent*distance(0,0,width/2,height/2)/100:
   mixPercent*distance(0,0,width/2,height/2)/100,           
   radius==0?0:blur,
   0.5, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0);
  return KeyMix(in1, in2, RGrad1, 1, "A", 100, reverse);
}

nfxDefMixer("radialWipe", "RadialWipe()");

I save all of this as a .h file in my startup directory.

As a final touch, I open a ui .h file and add an on/off button for the reverse parameter:

nuxDefExprToggle("RadialWipe.reverse");

Now when you relaunch Shake, you have a new mixer in Transition available to you.

Parameters
Type
Defaults
Function
overlap
int
0 The amount that the second clip is shifted earlier (to the left) to provide overlap of the two clips
mixer string "cut" Other default choices are "horizontalWipe", "verticalWipe" and "dissolve". However, you can add your own effects.
blur float 0 This appears for horizontal and verticalWipe, and is used to soften the wiping edge.
reverse int 0 This appears for horizontal and verticalWipe, and is used to flip the direction, i.e., from left-to-right to right-to-left.

Synopsis

 image Transition( 
  image i1,
  image i2,
  int overlap,
  const char * mixer,
  ....
 );

Script

 image = Transition( 
  i1,
  i2,
  overlap,
  "mixer",
  ....
 );