From 44ccd5f73ca9a6849b958c3824bf98cefa4486e7 Mon Sep 17 00:00:00 2001 From: Robin Fernandes Date: Fri, 21 Jul 2023 00:47:37 +1000 Subject: [PATCH] Add info_match_since() amd info_match_until() --- src/FunctionDoc.tsx | 81 +++++++++++++++++++ src/parseq-lang/parseq-lang-ast.ts | 1 + src/parseq-lang/parseq-lang-functions-info.ts | 34 +++++++- 3 files changed, 112 insertions(+), 4 deletions(-) diff --git a/src/FunctionDoc.tsx b/src/FunctionDoc.tsx index 62058a1..7a911d1 100644 --- a/src/FunctionDoc.tsx +++ b/src/FunctionDoc.tsx @@ -321,6 +321,23 @@ const FunctionDoc = () => { ] }], }, + { + category: "Context variables", + name: "**final_frame**: the last frame", + examples: [{ + keyframeOverrides: [ + { frame: 0, translation_z_i: 'final_frame', }, + { frame: 99, }, + ], + }, + { + description: "Make something happen half-way through the animation, regardless of the length.", + keyframeOverrides: [ + { frame: 0, translation_z: 0, translation_z_i: 'if (f < final_frame/2) L else (L+sin(p=2b, a=20))', }, + { frame: 99, translation_z: 100 }, + ], + }], + }, { category: "Context variables", name: "**prev_computed_value**: Value computed at the previous frame.", @@ -783,6 +800,70 @@ const FunctionDoc = () => { } ] }, + { + category: "Info matching", + name: "**info_match_since()**: frames since previous match", + function_ref: functionLibrary.info_match_since, + examples: [ + { + keyframeOverrides: [ + { frame: 0, info: "bassdrum 1", translation_z_i: 'info_match_since("bassdrum")' }, + { frame: 15, }, + { frame: 30, }, + { frame: 45, info: "bassdrum 2", }, + { frame: 60, info: "bassdrum 3", }, + { frame: 75, }, + { frame: 90, info: "bassdrum 4", }, + { frame: 99, }, + ] + }, + { + description: "Hold a value for half a beat after every matching keyframe", + keyframeOverrides: [ + { frame: 0, info: "bassdrum 1", translation_z_i: 'if info_match_since("bassdrum")<0.5b 100 else 0 ' }, + { frame: 15, }, + { frame: 30, }, + { frame: 45, info: "bassdrum 2", }, + { frame: 60, info: "bassdrum 3", }, + { frame: 75, }, + { frame: 90, info: "bassdrum 4", }, + { frame: 99}, + ] + } + ] + }, + { + category: "Info matching", + name: "**info_match_until()**: frames until next match", + function_ref: functionLibrary.info_match_until, + examples: [ + { + keyframeOverrides: [ + { frame: 0, info: "bassdrum 1", translation_z_i: 'info_match_until("bassdrum")' }, + { frame: 15, }, + { frame: 30, }, + { frame: 45, info: "bassdrum 2", }, + { frame: 60, info: "bassdrum 3", }, + { frame: 75, }, + { frame: 90, info: "bassdrum 4", }, + { frame: 99, }, + ] + }, + { + description: "Hold a value for half a beat before every matching keyframe", + keyframeOverrides: [ + { frame: 0, info: "bassdrum 1", translation_z_i: 'if info_match_until("bassdrum", 10)<0.5b 100 else 0' }, + { frame: 15, }, + { frame: 30, }, + { frame: 45, info: "bassdrum 2", }, + { frame: 60, info: "bassdrum 3", }, + { frame: 75, }, + { frame: 90, info: "bassdrum 4", }, + { frame: 99}, + ] + } + ] + }, { category: "Beats and seconds", name: "**Units**: ", diff --git a/src/parseq-lang/parseq-lang-ast.ts b/src/parseq-lang/parseq-lang-ast.ts index 37b68c8..d05a219 100644 --- a/src/parseq-lang/parseq-lang-ast.ts +++ b/src/parseq-lang/parseq-lang-ast.ts @@ -224,6 +224,7 @@ export class VariableReferenceAst extends ParseqAstNode { return cubicSplineInterpolation(ctx.definedFrames, ctx.definedValues, ctx.frame); case 'f': return ctx.frame; + case 'final_frame': case 'last_frame': return _.maxBy(ctx.allKeyframes, 'frame')?.frame ?? 0; case 'b': diff --git a/src/parseq-lang/parseq-lang-functions-info.ts b/src/parseq-lang/parseq-lang-functions-info.ts index 9166e2b..a8fbac6 100644 --- a/src/parseq-lang/parseq-lang-functions-info.ts +++ b/src/parseq-lang/parseq-lang-functions-info.ts @@ -58,7 +58,7 @@ const functionLibrary: { [key: string]: ParseqFunction } = { description: "Returns the frame number of the next keyframe that matched the regex, or -1 if none.", argDefs: [ { description: "regex", names: ["regex", "r"], type: "string", required: true, default: "" }, - { description: "default if no next match", names: ["default", "d"], type: "string", required: false, default: -1 } + { description: "default if no next match", names: ["default", "d"], type: "number", required: false, default: -1 } ], call: (ctx, args) => { const pattern = String(args[0]); @@ -71,7 +71,7 @@ const functionLibrary: { [key: string]: ParseqFunction } = { description: "Returns the number of frames between the previous and next match (equivalent to `info_match_next()-info_match_prev()`), or `-1` if not between matches.", argDefs: [ { description: "regex", names: ["regex", "r"], type: "string", required: true, default: "" }, - { description: "default if not between matches", names: ["default", "d"], type: "string", required: false, default: -1 } + { description: "default if not between matches", names: ["default", "d"], type: "number", required: false, default: -1 } ], call: (ctx, args) => { const pattern = String(args[0]); @@ -85,7 +85,7 @@ const functionLibrary: { [key: string]: ParseqFunction } = { description: "Returns a number between 0 and 1 reprenting how far the current frame is along the gap between (equivalent to `(f-info_match_prev()/info_match_gap())`, or `-1` if not between matches.", argDefs: [ { description: "regex", names: ["regex", "r"], type: "string", required: true, default: "" }, - { description: "default if not between matches", names: ["default", "d"], type: "string", required: false, default: -1 } + { description: "default if not between matches", names: ["default", "d"], type: "number", required: false, default: -1 } ], call: (ctx, args) => { const pattern = String(args[0]); @@ -93,7 +93,33 @@ const functionLibrary: { [key: string]: ParseqFunction } = { const nextMatch = frameOfNextMatch(ctx, pattern) return (nextMatch && prevMatch) ? (ctx.frame - prevMatch.frame) / (nextMatch.frame - prevMatch.frame) : Number(args[1]); } - } + }, + + "info_match_since": { + description: "Returns the number of frames since the previous keyframe that matched the regex, or an overridable fallback defaulting to `-1` if no next match. Equivalent to `f-info_match_prev()`.", + argDefs: [ + { description: "regex", names: ["regex", "r"], type: "string", required: true, default: "" }, + { description: "default if not between matches", names: ["default", "d"], type: "number", required: false, default: -1 } + ], + call: (ctx, args) => { + const pattern = String(args[0]); + const prevMatch = frameOfPrevMatch(ctx, pattern); + return (prevMatch) ? (ctx.frame - prevMatch.frame) : Number(args[1]); + } + }, + + "info_match_until": { + description: "Returns the number of frames until the next keyframe that matches the regex, or an overridable fallback defaulting to `-1` if no next match. Equivalent to `info_match_next()-f`.", + argDefs: [ + { description: "regex", names: ["regex", "r"], type: "string", required: true, default: "" }, + { description: "default if not between matches", names: ["default", "d"], type: "number", required: false, default: -1 } + ], + call: (ctx, args) => { + const pattern = String(args[0]); + const nextMatch = frameOfNextMatch(ctx, pattern) + return (nextMatch) ? (nextMatch.frame - ctx.frame) : Number(args[1]); + } + }, }