Need help in a script which can distribute & make loop of exiting keys

iEmby wrote on 1/24/2026, 8:12 AM

Hi everyone

hello @jetdv sir..

i just created this script via help of ChatGPT.

only one button is working well.

others are giving errors..

 

✔️Distribute exiting keyframes of PAN & CROP via equal distance. (replacement not adding any new)

✖️Distribute exiting keyframes of OFX via equal distance. (replacement not adding any new)

✖️Making loop from exiting 2 keyframes from both OFX / PAN & CROP via copying both exiting keyframes and placing forward up to user's requirement. if existing keys are A B and user want 5 times loop then (A B A B A B A B A B A) considering A-B-A is one loop

please help me fixing these..

thanks in advance..

using System;
using System.Collections.Generic;
using System.Windows.Forms;
using ScriptPortal.Vegas;

public class EntryPoint
{
    public void FromVegas(Vegas vegas)
    {
        Application.EnableVisualStyles();
        Application.Run(new MainForm(vegas));
    }
}

public class MainForm : Form
{
    private Vegas vegas;
    private Button btnPanCrop;
    private Button btnOFX;

    public MainForm(Vegas v)
    {
        vegas = v;

        Text = "Keyframe Redistributor";
        Width = 300;
        Height = 150;
        FormBorderStyle = FormBorderStyle.FixedDialog;
        MaximizeBox = false;

        btnPanCrop = new Button()
        {
            Text = "Redistribute Pan/Crop",
            Width = 220,
            Height = 30,
            Top = 15,
            Left = 30
        };

        Button btnMakeLoop = new Button()
        {
            Text = "Make Pan/Crop Loop",
            Width = 220,
            Height = 30,
            Top = 95,
            Left = 30
        };

        btnMakeLoop.Click += (s, e) => MakePanCropLoop();
        Controls.Add(btnMakeLoop);


        btnOFX = new Button()
        {
            Text = "Redistribute OFX",
            Width = 220,
            Height = 30,
            Top = 55,
            Left = 30
        };

        btnPanCrop.Click += (s, e) => RedistributePanCropKeys();
        btnOFX.Click += (s, e) => RedistributeOFXKeys();

        Controls.Add(btnPanCrop);
        Controls.Add(btnOFX);
    }

    private void MakePanCropLoop()
    {
        string input = PromptLoops();
        if (input == null) return;

        int loops;
        if (!int.TryParse(input, out loops) || loops < 1)
        {
            MessageBox.Show("Invalid loop count.");
            return;
        }

        foreach (Track track in vegas.Project.Tracks)
        {
            if (!track.IsVideo()) continue;

            foreach (VideoEvent ev in track.Events)
            {
                if (!ev.Selected) continue;

                var keys = ev.VideoMotion.Keyframes;

                if (keys.Count < 2)
                {
                    MessageBox.Show("Pan/Crop must have at least 2 keyframes (A & B).");
                    return;
                }

                // base keys
                VideoMotionKeyframe A = keys[0];
                VideoMotionKeyframe B = keys[1];

                // clear all existing keys
                while (keys.Count > 0)
                    keys.RemoveAt(0);

                int totalKeys = loops * 2;
                double step = ev.Length.ToMilliseconds() / (totalKeys - 1);

                for (int i = 0; i < loops; i++)
                {
                    // ---- A ----
                    int idxA = keys.Add(Timecode.FromMilliseconds(step * (i * 2)));
                    keys[idxA].Bounds = A.Bounds;
                    keys[idxA].Rotation = A.Rotation;
                    keys[idxA].Smoothness = A.Smoothness;

                    // ---- B ----
                    int idxB = keys.Add(Timecode.FromMilliseconds(step * (i * 2 + 1)));
                    keys[idxB].Bounds = B.Bounds;
                    keys[idxB].Rotation = B.Rotation;
                    keys[idxB].Smoothness = B.Smoothness;
                }
            }
        }

        MessageBox.Show("Pan/Crop loop created (A B copied & distributed).");
    }




    private string PromptLoops()
    {
        Form f = new Form();
        f.Text = "Make Loop";
        f.Width = 260;
        f.Height = 120;
        f.FormBorderStyle = FormBorderStyle.FixedDialog;
        f.StartPosition = FormStartPosition.CenterScreen;

        Label lbl = new Label();
        lbl.Text = "How many loops? (A B = 1 loop)";
        lbl.Left = 10;
        lbl.Top = 10;
        lbl.Width = 220;

        TextBox tb = new TextBox();
        tb.Left = 10;
        tb.Top = 35;
        tb.Width = 220;
        tb.Text = "3";

        Button ok = new Button();
        ok.Text = "OK";
        ok.Left = 155;
        ok.Top = 65;
        ok.DialogResult = DialogResult.OK;

        f.Controls.Add(lbl);
        f.Controls.Add(tb);
        f.Controls.Add(ok);
        f.AcceptButton = ok;

        return f.ShowDialog() == DialogResult.OK ? tb.Text : null;
    }


    // ================= PAN / CROP =================
    private void RedistributePanCropKeys()
    {
        bool found = false;

        foreach (Track track in vegas.Project.Tracks)
        {
            if (!track.IsVideo()) continue;

            foreach (VideoEvent ev in track.Events)
            {
                if (!ev.Selected) continue;

                var keys = ev.VideoMotion.Keyframes;
                int count = keys.Count;
                if (count < 2) continue;

                double step = ev.Length.ToMilliseconds() / (count - 1);

                for (int i = 0; i < count; i++)
                {
                    keys[i].Position = Timecode.FromMilliseconds(step * i);
                }

                found = true;
            }
        }

        MessageBox.Show(
            found ? "Pan/Crop keyframes redistributed." : "No valid Pan/Crop keys found.",
            "Done",
            MessageBoxButtons.OK,
            MessageBoxIcon.Information
        );
    }

    // ================= OFX =================
    private void RedistributeOFXKeys()
    {
        bool found = false;

        foreach (Track track in vegas.Project.Tracks)
        {
            if (!track.IsVideo()) continue;

            foreach (VideoEvent ev in track.Events)
            {
                if (!ev.Selected) continue;

                foreach (Effect eff in ev.Effects)
                {
                    if (!eff.IsOFX) continue;

                    OFXEffect ofx = null;

                    // 🔥 THIS IS THE FIX
                    try
                    {
                        ofx = eff.OFXEffect;
                    }
                    catch
                    {
                        continue; // skip broken / inaccessible OFX
                    }

                    if (ofx == null) continue;

                    foreach (OFXParameter parm in ofx.Parameters)
                    {
                        if (!parm.IsAnimated) continue;

                        Timecode len = ev.Length;

                        try
                        {
                            switch (parm.ParameterType)
                            {
                                case OFXParameterType.Double:
                                    RedistributeOFX(((OFXDoubleParameter)ofx[parm.Name]).Keyframes, len); found = true; break;

                                case OFXParameterType.Double2D:
                                    RedistributeOFX(((OFXDouble2DParameter)ofx[parm.Name]).Keyframes, len); found = true; break;

                                case OFXParameterType.Double3D:
                                    RedistributeOFX(((OFXDouble3DParameter)ofx[parm.Name]).Keyframes, len); found = true; break;

                                case OFXParameterType.RGB:
                                    RedistributeOFX(((OFXRGBParameter)ofx[parm.Name]).Keyframes, len); found = true; break;

                                case OFXParameterType.RGBA:
                                    RedistributeOFX(((OFXRGBAParameter)ofx[parm.Name]).Keyframes, len); found = true; break;

                                case OFXParameterType.Integer:
                                    RedistributeOFX(((OFXIntegerParameter)ofx[parm.Name]).Keyframes, len); found = true; break;

                                case OFXParameterType.Boolean:
                                    RedistributeOFX(((OFXBooleanParameter)ofx[parm.Name]).Keyframes, len); found = true; break;

                                case OFXParameterType.Choice:
                                    RedistributeOFX(((OFXChoiceParameter)ofx[parm.Name]).Keyframes, len); found = true; break;
                            }
                        }
                        catch
                        {
                            // skip individual broken parameter
                            continue;
                        }
                    }
                }
            }
        }

        MessageBox.Show(
            found ? "OFX keyframes redistributed safely." : "No usable animated OFX parameters found.",
            "Done",
            MessageBoxButtons.OK,
            MessageBoxIcon.Information
        );
    }


    // ================= GENERIC OFX HELPER =================
    private void RedistributeOFX<T>(IList<T> keys, Timecode len) where T : OFXKeyframe
    {
        int count = keys.Count;
        if (count < 2) return;

        double step = len.ToMilliseconds() / (count - 1);

        for (int i = 0; i < count; i++)
        {
            keys[i].Time = Timecode.FromMilliseconds(step * i);
        }
    }
}


 

Last changed by iEmby

PROCESSOR
     

Operating System: Windows 11 Pro 64-bit (Always Updated)
System Manufacturer: ASUS
12th Gen Intel(R) Core(TM) i7-12700 (20 CPUs), ~2.1GHz - 4.90GHz
Memory: 32GB RAM
Page File: 11134MB used, 7934MB Available
DirectX Version: DirectX 12

-----------------------------------------------

MOTHERBOARD

 

ASUS PRIME H610-CS D4
Intel® H610 (LGA 1700)
Ready for 12th Gen Intel® Processors
Micro-ATX Motherboard with DDR4
Realtek 1 Gb Ethernet
PCH Heatsink
PCIe 4.0 | M.2 slot (32Gbps) 
HDMI® | D-Sub | USB 3.2 Gen 1 ports
SATA 6 Gbps | COM header
LPT header | TPM header
Luminous Anti-Moisture Coating
5X Protection III
(Multiple Hardware Safeguards
For all-round protection)

-----------------------------------------------
EXTERNAL GRAPHIC CARD

-----------------------------------------------

INTERNAL GRAPHIC CARD (iGPU)

------------------------------------------------

LED - MONITOR

Monitor Name: Generic PnP Monitor
Monitor Model: HP 22es
Monitor Id: HWP331B
Native Mode: 1920 x 1080(p) (60.000Hz)
Output Type: HDMI

-----------------------------------------------

STORAGE DRIVE

Drive: C:
Free Space: 182.3 GB
Total Space: 253.9 GB
File System: NTFS
Model: WD Blue SN570 1TB (NVMe)

---------------O----------------

My System Info (PDF File).

https://drive.google.com/open?id=1-eoLmuXzshTRH_8RunAYAuNocKpiLoiV&usp=drive_fs

 

Also Check

Some useful creations by me including VEGAS Scripts

https://getopensofts.blogspot.com/

 

My YouTube Channel Dedicated to Only VEGAS Pro Tutorials

EDITROOM : My YouTube Channel (For VEGAS Tutorials)

Comments

zzzzzz9125 wrote on 1/24/2026, 8:29 AM

@iEmby It's indeed a very annoying problem. You can see another post: https://www.vegascreativesoftware.info/us/forum/reverse-pan-crop-keyframes-script--146655/#ca920774

I think it's because in the foreach loop, changing the Position of a keyframe also causes its index in the list to change. That is, the index of the keyframes in the list is completely determined by the Position order of them. This problem is a bit tricky, and too difficult to solve it with normal logic. I wonder if there's a solution.

Finally, I solved this problem by using a temp event: https://www.vegascreativesoftware.info/us/forum/reverse-pan-crop-keyframes-script--146655/#ca920789

VideoEvent tmpEvent = (VideoEvent)vEvent.Copy(myTrack, vEvent.Start);
...
myTrack.Events.Remove(tmpEvent);

I cleared all the keyframes of the current event, and then re-generated the keyframes completely based on the keyframes of tmpEvent.

In your script, you attempted to change the Position of Pan/Crop keyframes within the "for" loop, and this also caused an issue.

This is a hint. You can think about how to revise your script. I don't have more time to test yours yet.