Skip to content
Extraits de code Groupes Projets
Vérifiée Valider bba87e98 rédigé par Kubat's avatar Kubat
Parcourir les fichiers

Add 'Maël Martin' to programers and the cmove by Loïc Allègre

parent ef4f0413
Aucune branche associée trouvée
Aucune étiquette associée trouvée
1 requête de fusion!8Create an AppImage
include("karaskel.lua")
script_name = "Complex movements"
script_description = "Create complex (non-linear) movement effects defined by a cubic Bezier curve"
script_author = "Sting"
script_version = "1.1"
function movement(subs)
aegisub.progress.task("Retrieving header data...")
aegisub.progress.task("Applying effect...")
local i, ai, maxi, maxai = 1, 1, #subs, #subs -- Initial number of lines to process
while i <= maxi do -- For each line in the subs
aegisub.progress.task(string.format("Applying effect (%d/%d)...", ai, maxai))
aegisub.progress.set((ai-1)/maxai*100)
local l = subs[i]
if l.class == "dialogue" and -- If the line is a non-comment non-header line
not l.comment then
apply_cmove(subs, l) -- Apply the complex move transform
subs.delete(i) -- Delete this line (or comment it instead if you prefer)
maxi = maxi - 1 -- We deleted a line, so the loop upper bound must be decreased (to avoid endless loops)
else
i = i + 1 -- Nothing to see, skip to next line
end
ai = ai + 1
end
aegisub.progress.task("Finished!")
aegisub.progress.set(100)
aegisub.set_undo_point("Complex movement") -- Set undo point (for Aegisub) after the task is completed.
end
-- Transform complex \cmove in a series of simpler \move
function apply_cmove(subs, line)
local l = table.copy(line) -- Deep copy of the original line
local cmove = get_cmove_args(l) -- Retrieve the \cmove tag with its parameters
if cmove == nil then
subs.append(l) -- If no \cmove on that line, leave it unchanged
else
local time_array = {}
local real_time_array = {}
local curve_points = {}
local n = tonumber(cmove["steps"]) -- Number of point to approximate the curve with
local animations = get_anims(l) -- Retrieve all \t animation tags
local speed_profile, err = load(cmove["speed"])
local ok, apply_accel = pcall(speed_profile)
for i=0, n do -- Create arrays necessary for movement approximation :
time_array[i] = i/n
real_time_array[i] = (tonumber(cmove["t2"]) - tonumber(cmove["t1"]))*apply_accel(i/n) + tonumber(cmove["t1"]) -- Time interval subdivisions
curve_points[i] = {}
curve_points[i][1], curve_points[i][2] = bezier(time_array[i], get_bezier_points(cmove["command"])) -- Curve approximation points
end
-- For each subdivision of the movement approximation, create a new line between times t[i] and t[i+1], and moving between points i and i+1
for i=0, (n - 1) do
-- Copy the line text. We do not want to change whatever else is there.
l.text = line.text
-- Assembling the \move tag for the current subdivision. No timestamps specified, move duration is the whole subdivision duration
local move = string.format("\\move(%f,%f,%f,%f)", curve_points[i][1], curve_points[i][2], curve_points[i+1][1], curve_points[i+1][2])
-- Set start/end times of the line to the start/end times of the subdivision
l.start_time, l.end_time = line.start_time + real_time_array[i], line.start_time + real_time_array[i+1]
-- Replace the \cmove in the line text with the appropriate \move tag we just created
l.text, nb = string.gsub(l.text,'\\cmove%(([+-]?[%d%.]+)%s*,([+-]?[%d%.]+)%s*,([+-]?[%d%.]+)%s*,([e%d%a%s%-%.]+)%s*,".+"%)',move)
-- If there are any \t animations, we have to shift their timings :
-- we want those animations to go once, over the entire complex movement, not n times, over every subdivision.
if animations ~= nil then
local j = 1
-- For each \t tag on the line
while animations[j] ~= nil do
local anim = animations[j]
-- Set new timestamps for the animation :
-- by deducing the start time of the subdivision relatively to the global line start time,
-- we ensure that all \t timestamps on all subdivisions are relative to the global line start time.
-- That way, we have the expected animation once, over the whole line.
local new_start_anim, new_end_anim = to_int(anim["t1"]) - real_time_array[i], to_int(anim["t2"]) - real_time_array[i]
if math.abs(new_end_anim) < 1 then
if new_end_anim >= 0 then -- This is to go around an annoying unconsistent behavior : if the \t end time is 0 (or rounded to 0),
new_end_anim = new_end_anim + 1 -- it will be treated as a \t with no timestamp, therefore over the whole subdivision line.
else
new_end_anim = new_end_anim - 1 -- So if we end up with 0, deduce or add 1 ms, no one will notice it, and it works as we want it to.
end
end
-- Lua standard regex is limited, this is just a workaround for nested \clip() in \t tags.
if anim["clip"] ~= nil then
local clip_params = string.match(anim["clip"], '\\i?clip%((.-)%)')
l.text, nb = string.gsub(l.text, '\\i?clip%(' .. clip_params .. '%)', '')
-- Build the new \t tag with shifted timestamps
local new_anim_tag = string.format("\\t(%f,%f,%s)", new_start_anim, new_end_anim, anim["clip"] .. anim["tags"])
-- Replace the \t tag with the new one
l.text, nb = string.gsub(l.text, '\\t%(' .. anim["t1"] .. '%s*,%s*' .. anim["t2"] .. '%s*,%s*' .. anim["tags"] .. '%)', new_anim_tag)
else
-- Same, but simpler since there's no \clip
local new_anim_tag = string.format("\\t(%f,%f,%s)", new_start_anim, new_end_anim, anim["tags"])
l.text, nb = string.gsub(l.text, '\\t%(' .. anim["t1"] .. '%s*,%s*' .. anim["t2"] .. '%s*,%s*' .. anim["tags"] .. '%)', new_anim_tag)
end
j = j + 1
end
end
subs.append(l) -- Finally, append the modified subdivision line
end
-- For cases when the \cmove start/end is not the line start/end
-- Add a static line from the cmove end to the end of the line.
l.text = line.text
local pos = string.format("\\pos(%f,%f)", curve_points[n][1], curve_points[n][2])
l.start_time, l.end_time = line.start_time + real_time_array[n], line.end_time
l.text, nb = string.gsub(l.text,'\\cmove%([+-]?[%d%.]+%s*,[+-]?[%d%.]+%s*,[+-]?[%d%.]+%s*,[e%d%a%s%-%.]+%s*,".+"%)', pos)
-- Not forgetting to shift animations timestamps here too
if animations ~= nil then
local j = 1
while animations[j] ~= nil do
local anim = table.copy(animations[j])
local new_start_anim, new_end_anim = to_int(anim["t1"]) - real_time_array[n], to_int(anim["t2"]) - real_time_array[n]
if math.abs(new_end_anim) < 1 then
if new_end_anim >= 0 then
new_end_anim = new_end_anim + 1
else
new_end_anim = new_end_anim - 1
end
end
if anim["clip"] ~= nil then
local clip_params = string.match(anim["clip"], '\\i?clip%((.-)%)')
l.text, nb = string.gsub(l.text, '\\i?clip%(' .. clip_params .. '%)', '')
local new_anim_tag = string.format("\\t(%f,%f,%s)", new_start_anim, new_end_anim, anim["clip"] .. anim["tags"])
l.text, nb = string.gsub(l.text, '\\t%(' .. anim["t1"] .. '%s*,%s*' .. anim["t2"] .. '%s*,%s*' .. anim["tags"] .. '%)', new_anim_tag)
else
local new_anim_tag = string.format("\\t(%f,%f,%s)", new_start_anim, new_end_anim, anim["tags"])
l.text, nb = string.gsub(l.text, '\\t%(' .. anim["t1"] .. '%s*,%s*' .. anim["t2"] .. '%s*,%s*' .. anim["tags"] .. '%)', new_anim_tag)
end
j = j + 1
end
end
subs.append(l)
-- And add a static line before the cmove.
-- Since that line's start time is the global line start time, no need to shift the \t timestamps (or rather, shift by 0ms)
l.text = line.text
pos = string.format("\\pos(%f,%f)", curve_points[0][1], curve_points[0][2])
l.start_time, l.end_time = line.start_time, line.start_time + real_time_array[0]
l.text, nb = string.gsub(l.text,'\\cmove%(([+-]?[%d%.]+)%s*,([+-]?[%d%.]+)%s*,([+-]?[%d%.]+)%s*,([e%d%a%s%-%.]+)%s*,".+"%)', pos)
subs.append(l)
end
l = nil
cmove = nil
animations = nil
end
-- Retrieve parameters of the \cmove tag in a table
function get_cmove_args(line)
-- match the \cmove tag if it exists
local cmove_tag = string.match(line.text, '\\cmove%([+-]?[%d%.%s]+,[+-]?[%d%.%s]+,%s?[+-]?[%d%.%s]+,[e%d%a%s%-%.]+,".+"%)')
if cmove_tag == nil then
return nil
else
local cmove = {}
-- If there is a \cmove, retrieve its parameters in a table
cmove["t1"], cmove["t2"], cmove["steps"], cmove["command"], cmove["speed"] = string.match(cmove_tag, '\\cmove%(([+-]?[%d%.]+)%s*,%s*([+-]?[%d%.]+)%s*,%s*([+-]?[%d%.]+)%s*,%s*([e%d%a%s%-%.]+)%s*,%s?"(.+)"%)')
return cmove
end
end
-- Retrieve all \t tags in the line with params in a table anims{anim1 {[t1,] [t2,] [accel,] tags}, ... }
function get_anims(line)
local anims = {}
local remaining_text = line.text
local i = 1
while string.match(remaining_text, '\\t%(.*%)') ~= nil do -- While there are \t tags we haven't seen yet
local anim = {}
local insert_clip = string.match(remaining_text, '\\t%([^()]-(\\i?clip%(.-%))[^()]-%)') -- Trying to find a nested \clip
if insert_clip ~= nil then
remaining_text, nb1 = string.gsub(remaining_text, '\\i?clip%(.-%)', '') -- If there is one, get it out
anim["clip"] = insert_clip -- And keep it somewhere
end
anim["t1"], anim["t2"], anim["tags"] = string.match(remaining_text, '\\t%(([+-]?[%d%.]+)%s*,%s*([+-]?[%d%.]+)%s*,%s*(.-)%)') -- Get the \t params
local anim_tag = '\\t%(' .. anim["t1"] .. '%s*,%s*' .. anim["t2"] .. '%s*,%s*' .. anim["tags"] .. '%)'
remaining_text, nb2 = string.gsub(remaining_text, anim_tag, '') -- We got that one, remove it from what we have yet to check
anim["remain"] = remaining_text -- Debug leftover, I'll remove it eventually.
anims[i] = table.copy(anim)
i = i + 1
end
return anims
end
-- Convert string to signed int
function to_int(str)
if string.sub(str, 1, 1) == "-" then
return (- tonumber(string.sub(str,2)))
else
return tonumber(str)
end
end
-- Get Bezier curve definition points from ASSDraw command
function get_bezier_points(command)
local x1, y1, x2, y2, x3, y3, x4, y4 = string.match(command, '^m%s(%--[e%-%d%.]+)%s(%--[e%-%d%.]+)%sb%s(%--[e%-%d%.]+)%s(%--[e%-%d%.]+)%s(%--[e%-%d%.]+)%s(%--[e%-%d%.]+)%s(%--[e%-%d%.]+)%s(%--[e%-%d%.]+)$')
return to_int(x1), to_int(y1), to_int(x2), to_int(y2), to_int(x3), to_int(y3), to_int(x4), to_int(y4)
end
-- Calculate point x,y at time t in [0,1] of a cubic Bezier curve defined by four (xi,yi) points
function bezier(t, x1, y1, x2, y2, x3, y3, x4, y4)
local x = (1-t)*(1-t)*(1-t)*x1 + 3*t*(1-t)*(1-t)*x2 + 3*t*t*(1-t)*x3 + t*t*t*x4
local y = (1-t)*(1-t)*(1-t)*y1 + 3*t*(1-t)*(1-t)*y2 + 3*t*t*(1-t)*y3 + t*t*t*y4
return x, y
end
aegisub.register_macro("Complex movements", "Create complex movement effects", movement)
...@@ -2,6 +2,7 @@ automation_dir = dataroot / 'automation' ...@@ -2,6 +2,7 @@ automation_dir = dataroot / 'automation'
install_data( install_data(
'autoload/cleantags-autoload.lua', 'autoload/cleantags-autoload.lua',
'autoload/complex-movement.lua',
'autoload/duetto-meika.lua', 'autoload/duetto-meika.lua',
'autoload/kara-templater.lua', 'autoload/kara-templater.lua',
'autoload/karaoke-adjust-1sec.lua', 'autoload/karaoke-adjust-1sec.lua',
......
...@@ -71,6 +71,7 @@ void ShowAboutDialog(wxWindow *parent) { ...@@ -71,6 +71,7 @@ void ShowAboutDialog(wxWindow *parent) {
" Rodrigo Braz Monteiro\n" " Rodrigo Braz Monteiro\n"
" Simone Cociancich\n" " Simone Cociancich\n"
" Thomas Goyne\n" " Thomas Goyne\n"
" Maël Martin\n"
"User manual written by:\n" "User manual written by:\n"
" Karl Blomster\n" " Karl Blomster\n"
" Niels Martin Hansen\n" " Niels Martin Hansen\n"
......
0% Chargement en cours ou .
You are about to add 0 people to the discussion. Proceed with caution.
Terminez d'abord l'édition de ce message.
Veuillez vous inscrire ou vous pour commenter