Keyframe acceleration script

johnmeyer wrote on 4/27/2006, 1:23 PM
This is a crude demonstration script that is meant to show the concept of how you can create an effect where a pan/crop keyframe gradually and smoothly accelerates. The idea is that sometimes you want the motion to start slowly and then gradually increase in speed, and sometimes you want the opposite.

This script is hard wired. However, those that make commercial scripts might want to take the concept (and you are welcome to the code) and add a UI that would let you specify a beginning and ending location for the event; and let the user choose from a variety of acceleration models, perhaps with slider controls. This demonstration code uses a simple arithmetic acceleration where the first frame moves by unit 1, the second keyframe by unit 2, the third by unit three, etc. It puts twenty keyframes, equally spaced, into the event, so the last keyframe is moving 20 times further than did the first keyframe.

Anyway, here it is, in all its raw, crude splendor ...
/** 
* This script will assign "max" keyframes to the each selected event on the first timeline.
* It then pans the event by a smoothly accelerating rate.
*
* Copyright 2006 - John H. Meyer
* Last revision, April 25, 2006
*
**/

import System;
import System.Collections;
import System.Text;
import System.IO;
import System.Drawing;
import System.Windows.Forms;
import Sony.Vegas;
var evnt : TrackEvent;
var zero : int = 0;
var max : int = 40; // Change this to increase the number of keyframes,
// and thereby increase smoothness.
// Don't make this larger than number of frames in
// shortest event (no bounds checking in this script).

try {

//Find the selected event
var track = FindSelectedTrack();
if (null == track) // Must select a track
throw "no selected track";
var eventEnum = new Enumerator(track.Events);
if (track.IsVideo()) { // Track must be a video track
while (!eventEnum.atEnd()) {
evnt = TrackEvent(eventEnum.item());
if (evnt.Selected) {
var vevnt = VideoEvent(evnt);
var EventLength = evnt.Length.ToMilliseconds();

// Check if there is only one keyframe. Don't proceed if keyframes already assigned.
var keycollection = vevnt.VideoMotion.Keyframes;
if (keycollection.Count <= 1){

var mediaStream = GetActiveMediaStream (evnt); // These lines get width of media
if (mediaStream) {
var videoStream = VideoStream (mediaStream);
}
var minkeywidth = (max*(max+1))/2;
var videoWidth = videoStream.Width;
var videoHeight = videoStream.Height;

for (var i = max; i >= 0; i--) {

// This next line is the function for how far to move each keyframe
// Change this line to change the acceleration

var offset = new VideoMotionVertex(float(((max-i)/minkeywidth)*videoWidth), float(0));

// Create the next keyframe equally spaced from last keyframe
var NewKeyTimecode = new Timecode(Timecode.FromMilliseconds(int(EventLength - EventLength * (i/max))));
var key2 = new VideoMotionKeyframe(NewKeyTimecode);
vevnt.VideoMotion.Keyframes.Add(key2); // Add the new key frame

if (i < max) {
key2.MoveBy(offset); // Actually apply the offset; Don't move first keyframe.
} // End if i<max

vevnt.VideoMotion.Keyframes[max-i].Smoothness = 0; // Turn off the stupid smoothness setting

} // End for i=max
} //End if keycollection
} //End if selected

eventEnum.moveNext();

} // End While eventEnum
} // End If track.IsVideo

else {
MessageBox.Show("You must select a video track");
}


} catch (e) {
MessageBox.Show(e);
}



function FindSelectedTrack() : Track {
var trackEnum = new Enumerator(Vegas.Project.Tracks);
while (!trackEnum.atEnd()) {
var track : Track = Track(trackEnum.item());
if (track.Selected) {
return track;
}
trackEnum.moveNext();
}
return null;
}


function GetActiveMediaStream (trackEvent : TrackEvent) {
try {
if ( ! trackEvent.ActiveTake.IsValid()) {
throw "empty or invalid take";
}

var media = Vegas.Project.MediaPool.Find (trackEvent.ActiveTake.MediaPath);

if (null == media) {
throw "missing media";
}

var mediaStream = media.Streams.GetItemByMediaType (MediaType.Video, trackEvent.ActiveTake.StreamIndex);

return mediaStream;
}
catch (e) {
//MessageBox.Show(e);
return null;
}
}



Comments

Tech Diver wrote on 5/3/2006, 11:47 AM
Thanks John, I do have an immediate application for this script. It will be very usefull to me. However, I need help with a slight modification of the equation:

var offset = new VideoMotionVertex(float(((max-i)/minkeywidth)*videoWidth), float(0));

Instead of panning the entire distance of "videoWidth", I would like to pan "(videoWidth - width_of_the_viewing_area)". However, I don't know what is the name of the variable that I need to use. The idea behind this is that I am viewing only a subset of a high-resolution background image, and want to stop panning when I reach the right edge.
jetdv wrote on 5/3/2006, 12:10 PM
How about:

Vegas.Project.Video.Width

Tech Diver wrote on 5/3/2006, 12:51 PM
That didn't work for me, but looking at some other scripts I did get it to work with the following modifications indicated by the comment "added by Peter Honig". Please tell me it this is too convoluted and if there is a more elegant way of doing the same. By the way, where is there a list of all the objects that can be used in scripts? Am I missing some documentation?

[snip]
var minkeywidth = (max*(max+1))/2;
var videoWidth = videoStream.Width;
var videoHeight = videoStream.Height;
var firstKey = keycollection[0]; // added by Peter Honig
var cropWidth = firstKey.BottomRight.X - firstKey.TopLeft.X; // added by Peter Honig
var cropHeight = firstKey.BottomRight.Y - firstKey.TopLeft.Y; // added by Peter Honig
var workingWidth = videoWidth - cropWidth // added by Peter Honig

for (var i = max; i >= 0; i--) {

// This next line is the function for how far to move each keyframe
// Change this line to change the acceleration
// added by Peter Honig
var offset = new VideoMotionVertex(float(((max-i)/minkeywidth)*workingWidth), float(0));
[snip]
jetdv wrote on 5/3/2006, 1:05 PM
Have you downloaded this:
http://www.sonymediasoftware.com/download/step2.asp?DID=635

It includes the API in an HTML file.
Tech Diver wrote on 5/3/2006, 1:14 PM
I already had that, but upon second inspection realized that I overlooked the most important part! Namely, the HTML manual. That is exactly what I was looking for. I somehow managed to extract only the .js files when I originally unzipped the file. Thanks.
johnmeyer wrote on 5/3/2006, 2:51 PM
I don't think Video.Project.Width will be useful because the panning relates to the resolution of each piece of media. This is why I used these variables:

var videoWidth = videoStream.Width;
var videoHeight = videoStream.Height;

and added all that complex code (required because of the way Vegas internals work -- thank goodness for all the code snippets people posted -- this is NOT in the API document).

Thus, you start with this information for each event, and then make up whatever equation you want to use to move the media.

Unfortunately, I can't quite visualize what you want to do. You want to pan "(videoWidth - width_of_the_viewing_area)". If I have a picture that is 2000 pixels wide, and a viewing area that is 720 pixels wide, are you saying that you want to pan by 2000-720 = 1280 pixels? Not sure I understand.

At any rate, I think the "secret" you are looking for lies in this part of the code:

          var mediaStream = GetActiveMediaStream (evnt); // These lines get width of media
if (mediaStream) {
var videoStream = VideoStream (mediaStream);
}
var minkeywidth = (max*(max+1))/2;
var videoWidth = videoStream.Width;
var videoHeight = videoStream.Height;



Tech Diver wrote on 5/3/2006, 3:11 PM
What I am doing is setting my cropping rectangle SMALLER than the background image (I'm Zooming in) and panning without running off the backgound. That is, I stop panning as soon as the right edge of the cropping rectangle hits the right edge of the entire scene. My modification of your script succeeds in preventing "run-off" because it subtacts the width of the cropping rectangle (zoomed area) from the width of the entire videoWidth, leaving what I refer to as the workingWidth.

I hope I have succeeded in conveying what I am doing. If not, try my version of your scipt the following way: Open the Pan/Zoom dialog box and zoom in. Now align the left side of the rectangle with the left side of the original image. Run your script with my modifications and watch the effect. Panning is from one edge to the other.