[aegisub] more macros
This commit is contained in:
parent
be1a5af335
commit
4c481ffc38
235
.aegisub/automation/autoload/lyger.CircleText.lua
Normal file
235
.aegisub/automation/autoload/lyger.CircleText.lua
Normal file
|
@ -0,0 +1,235 @@
|
|||
--[[
|
||||
==README==
|
||||
|
||||
Circular Text
|
||||
|
||||
Define an origin, and this will put the text on a circular arc centered on that origin.
|
||||
|
||||
An origin must be defined and it must be different from the position of the line. You can't have
|
||||
a circle if the radius is zero.
|
||||
|
||||
The x coordinate of the position tag should match the x coordinate of the origin tag for best
|
||||
results. In other words, your original line should be at a right angle to the radius. Note that
|
||||
these are the x coordinates in the tags, not the x coordinates on screen, which will change if
|
||||
you rotate the tag.
|
||||
|
||||
Supports varied fonts, font sizes, font spacings, and x/y scales in the same line.
|
||||
|
||||
The resulting arc will be centered on the original rotation of your line.
|
||||
|
||||
Only works on static lines. If you want the line to move or rotate, use another macro.
|
||||
|
||||
|
||||
]]--
|
||||
|
||||
script_name = "Circular text"
|
||||
script_description = "Puts the text on a circular arc centered on the origin."
|
||||
script_version = "0.2.0"
|
||||
script_author = "lyger"
|
||||
script_namespace = "lyger.CircleText"
|
||||
|
||||
local DependencyControl = require("l0.DependencyControl")
|
||||
local rec = DependencyControl{
|
||||
feed = "https://raw.githubusercontent.com/TypesettingTools/lyger-Aegisub-Scripts/master/DependencyControl.json",
|
||||
{
|
||||
{"lyger.LibLyger", version = "2.0.0", url = "http://github.com/TypesettingTools/lyger-Aegisub-Scripts"},
|
||||
"aegisub.util"
|
||||
}
|
||||
}
|
||||
local LibLyger, util = rec:requireModules()
|
||||
local libLyger = LibLyger()
|
||||
|
||||
--[[
|
||||
Tags that can have any character after the tag declaration:
|
||||
\r
|
||||
\fn
|
||||
Otherwise, the first character after the tag declaration must be:
|
||||
a number, decimal point, open parentheses, minus sign, or ampersand
|
||||
]]--
|
||||
|
||||
|
||||
--Distance between two points
|
||||
local function distance(x1,y1,x2,y2)
|
||||
return math.sqrt((x2-x1)^2+(y2-y1)^2)
|
||||
end
|
||||
|
||||
--Sign of a value
|
||||
local function sign(n)
|
||||
return n/math.abs(n)
|
||||
end
|
||||
|
||||
--Angle in degrees, given the arc length and radius
|
||||
local function arc_angle(arc_length,radius)
|
||||
return arc_length/radius * 180/math.pi
|
||||
end
|
||||
|
||||
--Main processing function
|
||||
function circle_text(sub,sel)
|
||||
libLyger:set_sub(sub, sel)
|
||||
for si,li in ipairs(sel) do
|
||||
--Progress report
|
||||
aegisub.progress.task("Processing line "..si.."/"..#sel)
|
||||
aegisub.progress.set(100*si/#sel)
|
||||
|
||||
--Read in the line
|
||||
line = libLyger.lines[li]
|
||||
|
||||
--Get position and origin
|
||||
px, py = libLyger:get_pos(line)
|
||||
ox, oy = libLyger:get_org(line)
|
||||
|
||||
--Make sure pos and org are not the same
|
||||
if px==ox and py==oy then
|
||||
aegisub.log(1,"Error on line %d: Position and origin cannot be the same!",li)
|
||||
return
|
||||
end
|
||||
|
||||
--Get radius
|
||||
radius=distance(px,py,ox,oy)
|
||||
|
||||
--Remove \pos and \move
|
||||
--If your line was non-static, too bad
|
||||
line.text = LibLyger.line_exclude(line.text,{"pos","move"})
|
||||
|
||||
--Make sure line starts with a tag block
|
||||
if line.text:find("^{")==nil then
|
||||
line.text="{}"..line.text
|
||||
end
|
||||
|
||||
--Rotation direction: positive if each character adds to the angle,
|
||||
--negative if each character subtracts from the angle
|
||||
rot_dir=sign(py-oy)
|
||||
|
||||
--Add the \pos back with recalculated position
|
||||
line.text=line.text:gsub("^{",string.format("{\\pos(%d,%d)",ox,oy+rot_dir*radius))
|
||||
|
||||
--Get z rotation
|
||||
--Will only take the first one, because if you wanted the text to be on a circular arc,
|
||||
--why do you have more than one z rotation tag in the first place?
|
||||
_,_,zrot=line.text:find("\\frz([%-%.%d]+)")
|
||||
zrot=zrot or line.styleref.angle
|
||||
|
||||
--Make line table
|
||||
line_table={}
|
||||
for thistag,thistext in line.text:gmatch("({[^{}]*})([^{}]*)") do
|
||||
table.insert(line_table,{tag=thistag,text=thistext})
|
||||
end
|
||||
|
||||
--Where data on the character widths will be stored
|
||||
char_data={}
|
||||
|
||||
--Total width of line
|
||||
cum_width=0
|
||||
|
||||
--Stores current state of the line as style table
|
||||
current_style = util.deep_copy(line.styleref)
|
||||
|
||||
--First pass to collect data on character widths
|
||||
for i,val in ipairs(line_table) do
|
||||
|
||||
char_data[i]={}
|
||||
|
||||
--Fix style tables to reflect override tags
|
||||
local _,_,font_name=val.tag:find("\\fn([^\\{}]+)")
|
||||
local _,_,font_size=val.tag:find("\\fs([%-%.%d]+)")
|
||||
local _,_,font_scx=val.tag:find("\\fscx([%-%.%d]+)")
|
||||
local _,_,font_scy=val.tag:find("\\fscy([%-%.%d]+)")
|
||||
local _,_,font_sp=val.tag:find("\\fsp([%-%.%d]+)")
|
||||
local _,_,_bold=val.tag:find("\\b([01])")
|
||||
local _,_,_italic=val.tag:find("\\i([01])")
|
||||
|
||||
current_style.fontname=font_name or current_style.fontname
|
||||
current_style.fontsize=tonumber(font_size) or current_style.fontsize
|
||||
current_style.scale_x=tonumber(font_scx) or current_style.scale_x
|
||||
current_style.scale_y=tonumber(font_scy) or current_style.scale_y
|
||||
current_style.spacing=tonumber(font_sp) or current_style.spacing
|
||||
if _bold~=nil then
|
||||
if _bold=="1" then current_style.bold=true
|
||||
else current_style.bold=false end
|
||||
end
|
||||
if _italic~=nil then
|
||||
if _italic=="1" then current_style.italic=true
|
||||
else current_style.italic=false end
|
||||
end
|
||||
|
||||
val.style = util.deep_copy(current_style)
|
||||
|
||||
--Collect width data on each char
|
||||
for thischar in val.text:gmatch(".") do
|
||||
cwidth=aegisub.text_extents(val.style,thischar)
|
||||
table.insert(char_data[i],{char=thischar,width=cwidth})
|
||||
end
|
||||
|
||||
--Increment cumulative width
|
||||
cum_width=cum_width+aegisub.text_extents(val.style,val.text)
|
||||
|
||||
end
|
||||
|
||||
--The angle that the rotation will begin at
|
||||
start_angle=zrot-(rot_dir*arc_angle(cum_width,radius))/2
|
||||
|
||||
rebuilt_text=""
|
||||
cum_rot=0
|
||||
|
||||
--Second pass to rebuild line with new tags
|
||||
for i,val in ipairs(line_table) do
|
||||
|
||||
rebuilt_text=rebuilt_text..val.tag:gsub("\\fsp[%-%.%d]+",""):gsub("\\frz[%-%.%d]+","")
|
||||
|
||||
for k,tchar in ipairs(char_data[i]) do
|
||||
--Character spacing should be the average of this character's width and the next one's
|
||||
--For spacing, scale width back up by the character's relevant scale_x,
|
||||
--because \fsp scales too. Also, subtract the existing font spacing
|
||||
this_spacing=0
|
||||
this_width=0
|
||||
if k~=#char_data[i] then
|
||||
this_width=(tchar.width+char_data[i][k+1].width)/2
|
||||
this_spacing=-1*(this_width*100/val.style.scale_x-val.style.spacing)
|
||||
else
|
||||
this_width=i~=#line_table and (tchar.width+char_data[i+1][1].width)/2 or 0
|
||||
this_spacing=i~=#line_table
|
||||
and -1*((tchar.width*100/val.style.scale_x
|
||||
+ char_data[i+1][1].width*100/line_table[i+1].style.scale_x)/2
|
||||
-val.style.spacing)
|
||||
or 0
|
||||
end
|
||||
|
||||
rebuilt_text=rebuilt_text..string.format("{\\frz%.3f\\fsp%.2f}%s",
|
||||
(start_angle+rot_dir*cum_rot)%360,this_spacing,tchar.char)
|
||||
|
||||
cum_rot=cum_rot+arc_angle(this_width,radius)
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
--Fuck the re library. Maybe I'll come back to this
|
||||
whitespaces=re.find(rebuilt_text,
|
||||
'(\{\\\\frz[\\d\\.\\-]+\\\\fsp[\\d\\.\\-]+\}\\S)((?:\{\\\\frz[\\d\\.\\-]+\\\\fsp[\\d\\.\\-]+\}\\s)+)')
|
||||
|
||||
for j=1,#whitespaces-1,2 do
|
||||
first_tag=whitespaces[j].str
|
||||
other_tags=whitespaces[j+1].str
|
||||
aegisub.log("%s%s\n",first_tag,other_tags)
|
||||
first_space=first_tag:match("\\fsp([%d%.%-]+)")
|
||||
other_spaces=0
|
||||
total_wsp=0
|
||||
for _sp in other_tags:gmatch("\\fsp([%d%.%-]+)") do
|
||||
other_spaces=other_spaces+tonumber(_sp)
|
||||
total_wsp=total_wsp+1
|
||||
end
|
||||
total_space=tonumber(first_space)+other_spaces
|
||||
rebuilt_text=rebuilt_text:gsub(first_tag..other_tags,
|
||||
first_tag:gsub("\\fsp[%d%.%-]+",string.format("\\fsp%.2f",total_space))..string.rep(" ",total_wsp))
|
||||
end]]--
|
||||
|
||||
line.text=rebuilt_text:gsub("}{","")
|
||||
|
||||
sub[li]=line
|
||||
|
||||
end
|
||||
|
||||
aegisub.set_undo_point(script_name)
|
||||
|
||||
end
|
||||
|
||||
rec:registerMacro(circle_text)
|
472
.aegisub/automation/autoload/lyger.ClipBlur.lua
Normal file
472
.aegisub/automation/autoload/lyger.ClipBlur.lua
Normal file
|
@ -0,0 +1,472 @@
|
|||
--[[
|
||||
==README==
|
||||
|
||||
Blur clip
|
||||
|
||||
There's really not much to explain here. \clip statements produce a sharp edge. This script
|
||||
draws new \clip statements with decreasing alphas in order to imitate the effect of a blur.
|
||||
|
||||
The appearance won't always be perfect because of the limitations of precision with vector
|
||||
clip coordinates. The "precision" parameter ameliorates this somewhat, but the odd jagged
|
||||
line here and there is inevitable.
|
||||
|
||||
A note on the "precision" parameter: it scales exponentionally. If you want a 5-pixel blur,
|
||||
then a precision of 1 produces 6 lines (5 for the blur, 1 for the center). Precision 2 will
|
||||
generate 11 lines (10 for the blur, 1 for the center) and precision 3 will generate 21 lines
|
||||
(20 for the blur, 1 for the center). As you've probably figured out, a precision of 4 will
|
||||
create a whopping 41 lines. Use with caution.
|
||||
|
||||
|
||||
]]--
|
||||
script_name = "Blur clip"
|
||||
script_description = "Blurs a vector clip."
|
||||
script_version = "1.2.0"
|
||||
script_author = "lyger"
|
||||
script_namespace = "lyger.ClipBlur"
|
||||
|
||||
local DependencyControl = require("l0.DependencyControl")
|
||||
local rec = DependencyControl{
|
||||
feed = "https://raw.githubusercontent.com/TypesettingTools/lyger-Aegisub-Scripts/master/DependencyControl.json",
|
||||
{
|
||||
{"lyger.LibLyger", version = "2.0.0", url = "http://github.com/TypesettingTools/lyger-Aegisub-Scripts"},
|
||||
"aegisub.util"
|
||||
}
|
||||
}
|
||||
local LibLyger, util = rec:requireModules()
|
||||
local libLyger = LibLyger()
|
||||
|
||||
--Distance between two points
|
||||
local function distance(x1,y1,x2,y2)
|
||||
return math.sqrt((x2-x1)^2+(y2-y1)^2)
|
||||
end
|
||||
|
||||
--Sign of a value
|
||||
local function sign(n)
|
||||
return n/math.abs(n)
|
||||
end
|
||||
|
||||
--Haha I didn't know these functions existed. May as well just alias them
|
||||
local todegree=math.deg
|
||||
local torad=math.rad
|
||||
|
||||
--Parses vector shape and makes it into a table
|
||||
function make_vector_table(vstring)
|
||||
local vtable={}
|
||||
local vexp=vstring:match("^([1-4]),")
|
||||
vexp=tonumber(vexp) or 1
|
||||
for vtype,vcoords in vstring:gmatch("([mlb])([%d%s%-]+)") do
|
||||
for vx,vy in vcoords:gmatch("([%d%-]+)%s+([%d%-]+)") do
|
||||
table.insert(vtable,{["class"]=vtype,["x"]=tonumber(vx),["y"]=tonumber(vy)})
|
||||
end
|
||||
end
|
||||
return vtable,vexp
|
||||
end
|
||||
|
||||
--Reverses a vector table object
|
||||
function reverse_vector_table(vtable)
|
||||
local nvtable={}
|
||||
if #vtable<1 then return nvtable end
|
||||
--Make sure vtable does not end in an m. I don't know why this would happen but still
|
||||
maxi=#vtable
|
||||
while vtable[maxi].class=="m" do
|
||||
maxi=maxi-1
|
||||
end
|
||||
|
||||
--All vector shapes start with m
|
||||
nstart = util.copy(vtable[maxi])
|
||||
tclass=nstart.class
|
||||
nstart.class="m"
|
||||
table.insert(nvtable,nstart)
|
||||
|
||||
--Reinsert coords in backwards order, but shift the class over by 1
|
||||
--because that's how vector shapes behave in aegi
|
||||
for i=maxi-1,1,-1 do
|
||||
tcoord = util.copy(vtable[i])
|
||||
_temp=tcoord.class
|
||||
tcoord.class=tclass
|
||||
tclass=_temp
|
||||
table.insert(nvtable,tcoord)
|
||||
end
|
||||
|
||||
return nvtable
|
||||
end
|
||||
|
||||
--Turns vector table into string
|
||||
function vtable_to_string(vt)
|
||||
cclass=nil
|
||||
result=""
|
||||
|
||||
for i=1,#vt,1 do
|
||||
if vt[i].class~=cclass then
|
||||
result=result..string.format("%s %d %d ",vt[i].class,vt[i].x,vt[i].y)
|
||||
cclass=vt[i].class
|
||||
else
|
||||
result=result..string.format("%d %d ",vt[i].x,vt[i].y)
|
||||
end
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
--Rounds to the given number of decimal places
|
||||
function round(n,dec)
|
||||
dec=dec or 0
|
||||
return math.floor(n*10^dec+0.5)/(10^dec)
|
||||
end
|
||||
|
||||
--Grows vt outward by the radius r scaled by sc
|
||||
function grow(vt,r,sc)
|
||||
ch=get_chirality(vt)
|
||||
local wvt=wrap(vt)
|
||||
local nvt={}
|
||||
sc=sc or 1
|
||||
|
||||
--Grow
|
||||
for i=2,#wvt-1,1 do
|
||||
cpt=wvt[i]
|
||||
ppt=wvt[i].prev
|
||||
npt=wvt[i].next
|
||||
while distance(cpt.x,cpt.y,ppt.x,ppt.y)==0 do
|
||||
ppt=ppt.prev
|
||||
end
|
||||
while distance(cpt.x,cpt.y,npt.x,npt.y)==0 do
|
||||
npt=npt.prev
|
||||
end
|
||||
rot1=todegree(math.atan2(cpt.y-ppt.y,cpt.x-ppt.x))
|
||||
rot2=todegree(math.atan2(npt.y-cpt.y,npt.x-cpt.x))
|
||||
drot=(rot2-rot1)%360
|
||||
|
||||
--Angle to expand at
|
||||
nrot=(0.5*drot+90)%180
|
||||
if ch<0 then nrot=nrot+180 end
|
||||
|
||||
--Adjusted radius
|
||||
__ar=math.cos(torad(ch*90-nrot)) --<3
|
||||
ar=(__ar<0.00001 and r) or r/math.abs(__ar)
|
||||
|
||||
newx=cpt.x*sc
|
||||
newy=cpt.y*sc
|
||||
|
||||
if r~=0 then
|
||||
newx=newx+sc*round(ar*math.cos(torad(nrot+rot1)))
|
||||
newy=newy+sc*round(ar*math.sin(torad(nrot+rot1)))
|
||||
end
|
||||
|
||||
table.insert(nvt,{["class"]=cpt.class,
|
||||
["x"]=newx,
|
||||
["y"]=newy})
|
||||
end
|
||||
|
||||
--Check for "crossovers"
|
||||
--New data type to store points with same coordinates
|
||||
local mvt={}
|
||||
local wnvt=wrap(nvt)
|
||||
for i,p in ipairs(wnvt) do
|
||||
table.insert(mvt,{["class"]={p.class},["x"]=p.x,["y"]=p.y})
|
||||
end
|
||||
|
||||
--Number of merges so far
|
||||
merges=0
|
||||
|
||||
for i=2,#wnvt,1 do
|
||||
mi=i-merges
|
||||
dx=wvt[i].x-wvt[i-1].x
|
||||
dy=wvt[i].y-wvt[i-1].y
|
||||
ndx=wnvt[i].x-wnvt[i-1].x
|
||||
ndy=wnvt[i].y-wnvt[i-1].y
|
||||
|
||||
if (dy*ndy<0 or dx*ndx<0) then
|
||||
--Multiplicities
|
||||
c1=#mvt[mi-1].class
|
||||
c2=#mvt[mi].class
|
||||
|
||||
--Weighted average
|
||||
mvt[mi-1].x=(c1*mvt[mi-1].x+c2*mvt[mi].x)/(c1+c2)
|
||||
mvt[mi-1].y=(c1*mvt[mi-1].y+c2*mvt[mi].y)/(c1+c2)
|
||||
|
||||
--Merge classes
|
||||
mvt[mi-1].class={unpack(mvt[mi-1].class),unpack(mvt[mi].class)}
|
||||
|
||||
--Delete point
|
||||
table.remove(mvt,mi)
|
||||
merges=merges+1
|
||||
end
|
||||
end
|
||||
|
||||
--Rebuild wrapped new vector table
|
||||
wnvt={}
|
||||
for i,p in ipairs(mvt) do
|
||||
for k,pclass in ipairs(p.class) do
|
||||
table.insert(wnvt,{["class"]=pclass,["x"]=p.x,["y"]=p.y})
|
||||
end
|
||||
end
|
||||
|
||||
return unwrap(wnvt)
|
||||
end
|
||||
|
||||
function merge_identical(vt)
|
||||
local mvt = util.copy(vt)
|
||||
i=2
|
||||
lx=mvt[1].x
|
||||
ly=mvt[1].y
|
||||
while i<#mvt do
|
||||
if mvt[i].x==lx and mvt[i].y==ly then
|
||||
table.remove(mvt,i)
|
||||
else
|
||||
lx=mvt[i].x
|
||||
ly=mvt[i].y
|
||||
i=i+1
|
||||
end
|
||||
end
|
||||
return mvt
|
||||
end
|
||||
|
||||
--Returns chirality of vector shape. +1 if counterclockwise, -1 if clockwise
|
||||
function get_chirality(vt)
|
||||
local wvt=wrap(vt)
|
||||
wvt=merge_identical(wvt)
|
||||
trot=0
|
||||
for i=2,#wvt-1,1 do
|
||||
rot1=math.atan2(wvt[i].y-wvt[i-1].y,wvt[i].x-wvt[i-1].x)
|
||||
rot2=math.atan2(wvt[i+1].y-wvt[i].y,wvt[i+1].x-wvt[i].x)
|
||||
drot=todegree(rot2-rot1)%360
|
||||
if drot>180 then drot=360-drot elseif drot==180 then drot=0 else drot=-1*drot end
|
||||
trot=trot+drot
|
||||
end
|
||||
return sign(trot)
|
||||
end
|
||||
|
||||
--Duplicates first and last coordinates at the end and beginning of shape,
|
||||
--to allow for wraparound calculations
|
||||
function wrap(vt)
|
||||
local wvt={}
|
||||
table.insert(wvt,util.copy(vt[#vt]))
|
||||
for i=1,#vt,1 do
|
||||
table.insert(wvt,util.copy(vt[i]))
|
||||
end
|
||||
table.insert(wvt,util.copy(vt[1]))
|
||||
|
||||
--Add linked list capability. Because. Hacky fix gogogogo
|
||||
for i=2,#wvt-1 do
|
||||
wvt[i].prev=wvt[i-1]
|
||||
wvt[i].next=wvt[i+1]
|
||||
end
|
||||
--And link the start and end
|
||||
wvt[2].prev=wvt[#wvt-1]
|
||||
wvt[#wvt-1].next=wvt[2]
|
||||
|
||||
return wvt
|
||||
end
|
||||
|
||||
--Cuts off the first and last coordinates, to undo the effects of "wrap"
|
||||
function unwrap(wvt)
|
||||
local vt={}
|
||||
for i=2,#wvt-1,1 do
|
||||
table.insert(vt,util.copy(wvt[i]))
|
||||
end
|
||||
return vt
|
||||
end
|
||||
|
||||
--Main execution function
|
||||
function blur_clip(sub,sel)
|
||||
--GUI config
|
||||
config=
|
||||
{
|
||||
{
|
||||
class="label",
|
||||
label="Blur size:",
|
||||
x=0,y=0,width=1,height=1
|
||||
},
|
||||
{
|
||||
class="floatedit",
|
||||
name="bsize",
|
||||
min=0,step=0.5,value=1,
|
||||
x=1,y=0,width=1,height=1
|
||||
},
|
||||
{
|
||||
class="label",
|
||||
label="Blur position:",
|
||||
x=0,y=1,width=1,height=1
|
||||
},
|
||||
{
|
||||
class="dropdown",
|
||||
name="bpos",
|
||||
items={"outside","middle","inside"},
|
||||
value="outside",
|
||||
x=1,y=1,width=1,height=1
|
||||
},
|
||||
{
|
||||
class="label",
|
||||
label="Precision:",
|
||||
x=0,y=2,width=1,height=1
|
||||
},
|
||||
{
|
||||
class="intedit",
|
||||
name="bprec",
|
||||
min=1,max=4,value=2,
|
||||
x=1,y=2,width=1,height=1
|
||||
}
|
||||
}
|
||||
|
||||
--Show dialog
|
||||
pressed,results=aegisub.dialog.display(config,{"Go","Cancel"})
|
||||
if pressed=="Cancel" then aegisub.cancel() end
|
||||
|
||||
--Size of the blur
|
||||
bsize=results["bsize"]
|
||||
|
||||
--Scale exponent for all the numbers
|
||||
sexp=results["bprec"]
|
||||
|
||||
--How far to offset the blur by
|
||||
boffset=0
|
||||
if results["bpos"]=="inside" then boffset=bsize
|
||||
elseif results["bpos"]=="middle" then boffset=bsize/2 end
|
||||
|
||||
--How far to offset the next line read
|
||||
lines_added=0
|
||||
|
||||
libLyger:set_sub(sub, sel)
|
||||
for si,li in ipairs(sel) do
|
||||
--Progress report
|
||||
aegisub.progress.task("Processing line "..si.."/"..#sel)
|
||||
aegisub.progress.set(100*si/#sel)
|
||||
|
||||
--Read in the line
|
||||
line = libLyger.lines[li]
|
||||
|
||||
--Comment it out
|
||||
line.comment=true
|
||||
sub[li+lines_added]=line
|
||||
line.comment=false
|
||||
|
||||
--Find the clipping shape
|
||||
ctype,tvector=line.text:match("\\(i?clip)%(([^%(%)]+)%)")
|
||||
|
||||
--Cancel if it doesn't exist
|
||||
if tvector==nil then
|
||||
aegisub.log("Make sure all lines have a clip statement.")
|
||||
aegisub.cancel()
|
||||
end
|
||||
|
||||
--Get position and add
|
||||
px,py = libLyger:get_pos(line)
|
||||
if line.text:match("\\pos")==nil and line.text:match("\\move")==nil then
|
||||
line.text=string.format("{\\pos(%d,%d)}",px,py)..line.text
|
||||
end
|
||||
|
||||
--Round
|
||||
local function rnd(num)
|
||||
num=tonumber(num) or 0
|
||||
if num<0 then
|
||||
num=num-0.5
|
||||
return math.ceil(num)
|
||||
end
|
||||
num=num+0.5
|
||||
return math.floor(num)
|
||||
end
|
||||
--If it's a rectangular clip, convert to vector clip
|
||||
if tvector:match("([%d%-%.]+),([%d%-%.]+),([%d%-%.]+),([%d%-%.]+)")~=nil then
|
||||
_x1,_y1,_x2,_y2=tvector:match("([%d%-%.]+),([%d%-%.]+),([%d%-%.]+),([%d%-%.]+)")
|
||||
tvector=string.format("m %d %d l %d %d %d %d %d %d",
|
||||
rnd(_x1),rnd(_y1),rnd(_x2),rnd(_y1),rnd(_x2),rnd(_y2),rnd(_x1),rnd(_y2))
|
||||
end
|
||||
|
||||
--The original table and original scale exponent
|
||||
otable,oexp=make_vector_table(tvector)
|
||||
|
||||
--Effective scale and scale exponent
|
||||
eexp=sexp-oexp+1
|
||||
escale=2^(eexp-1)
|
||||
--aegisub.log("Escale: %.2f",escale)
|
||||
|
||||
--The innermost line
|
||||
iline = util.copy(line)
|
||||
itable={}
|
||||
if ctype=="iclip" then
|
||||
itable=grow(otable,bsize*2^(oexp-1)-boffset,escale)
|
||||
else
|
||||
itable=grow(otable,-1*boffset,escale)
|
||||
end
|
||||
iline.text=iline.text:gsub("\\i?clip%([^%(%)]+%)","\\"..ctype.."("..sexp..","..vtable_to_string(itable)..")")
|
||||
|
||||
--Add it to the subs
|
||||
sub.insert(li+lines_added+1,iline)
|
||||
lines_added=lines_added+1
|
||||
|
||||
--Set default alpha values
|
||||
dalpha={}
|
||||
dalpha[1]=alpha_from_style(line.styleref.color1)
|
||||
dalpha[2]=alpha_from_style(line.styleref.color2)
|
||||
dalpha[3]=alpha_from_style(line.styleref.color3)
|
||||
dalpha[4]=alpha_from_style(line.styleref.color4)
|
||||
|
||||
--First tag block
|
||||
ftag=line.text:match("^{[^{}]*}")
|
||||
if ftag==nil then
|
||||
ftag="{}"
|
||||
line.text="{}"..line.text
|
||||
end
|
||||
|
||||
--List of alphas not yet accounted for in the first tag
|
||||
unacc={}
|
||||
|
||||
if ftag:match("\\alpha")==nil then
|
||||
if ftag:match("\\1a")==nil then table.insert(unacc,1) end
|
||||
if ftag:match("\\2a")==nil then table.insert(unacc,2) end
|
||||
if ftag:match("\\3a")==nil then table.insert(unacc,3) end
|
||||
if ftag:match("\\4a")==nil then table.insert(unacc,4) end
|
||||
end
|
||||
|
||||
--Add tags if any are unaccounted for
|
||||
if #unacc>0 then
|
||||
--If all the unaccounted-for alphas are equal, only add an "alpha" tag
|
||||
_tempa=dalpha[unacc[1]]
|
||||
_equal=true
|
||||
for _k,_a in ipairs(unacc) do
|
||||
if dalpha[_a]~=_tempa then _equal=false end
|
||||
end
|
||||
|
||||
if _equal then line.text=line.text:gsub("^{","{\\alpha"..dalpha[unacc[1]])
|
||||
else
|
||||
for _k,ui in ipairs(unacc) do
|
||||
line.text=line.text:gsub("^{","{\\"..ui.."a"..dalpha[ui])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
prevclip=itable
|
||||
|
||||
for j=1,math.ceil(bsize*escale*2^(oexp-1)),1 do
|
||||
|
||||
--Interpolation factor
|
||||
factor=j/(bsize*escale+1)
|
||||
|
||||
--Flip if it's an iclip
|
||||
if ctype=="iclip" then factor=1-factor end
|
||||
|
||||
--Copy the line
|
||||
tline = util.copy(line)
|
||||
|
||||
--Sub in the interpolated alphas
|
||||
tline.text=tline.text:gsub("\\alpha([^\\{}]+)",
|
||||
function(a) return "\\alpha"..interpolate_alpha(factor,a,"&HFF&") end)
|
||||
tline.text=tline.text:gsub("\\([1-4]a)([^\\{}]+)",
|
||||
function(a,b) return "\\"..a..interpolate_alpha(factor,b,"&HFF&") end)
|
||||
|
||||
--Write the correct clip
|
||||
thisclip=grow(otable,j/escale-boffset,escale)
|
||||
clipstring=vtable_to_string(thisclip)..vtable_to_string(reverse_vector_table(prevclip))
|
||||
prevclip=thisclip
|
||||
|
||||
tline.text=tline.text:gsub("\\i?clip%([^%(%)]+%)","\\clip("..sexp..","..clipstring..")")
|
||||
|
||||
--Insert the line
|
||||
sub.insert(li+lines_added+1,tline)
|
||||
lines_added=lines_added+1
|
||||
end
|
||||
end
|
||||
aegisub.set_undo_point(script_name)
|
||||
end
|
||||
|
||||
rec:registerMacro(blur_clip)
|
77
.aegisub/automation/autoload/lyger.ClipShifter.lua
Normal file
77
.aegisub/automation/autoload/lyger.ClipShifter.lua
Normal file
|
@ -0,0 +1,77 @@
|
|||
--[[
|
||||
==README==
|
||||
|
||||
Put a rectangular clip in the first line.
|
||||
|
||||
Highlight that line and all the other lines you want to add the clip to.
|
||||
|
||||
Run, and the position-shifted clip will be added to those lines
|
||||
|
||||
]]--
|
||||
|
||||
script_name = "Clip shifter"
|
||||
script_description = "Reads a rectangular clip from the first line and places it on the other highlighted ones."
|
||||
script_version = "0.2.0"
|
||||
script_author = "lyger"
|
||||
script_namespace = "lyger.ClipShifter"
|
||||
|
||||
local DependencyControl = require("l0.DependencyControl")
|
||||
local rec = DependencyControl{
|
||||
feed = "https://raw.githubusercontent.com/TypesettingTools/lyger-Aegisub-Scripts/master/DependencyControl.json",
|
||||
{
|
||||
{"lyger.LibLyger", version = "2.0.0", url = "http://github.com/TypesettingTools/lyger-Aegisub-Scripts"},
|
||||
"aegisub.util"
|
||||
}
|
||||
}
|
||||
local LibLyger, util = rec:requireModules()
|
||||
local libLyger = LibLyger()
|
||||
|
||||
function clip_shift(sub,sel)
|
||||
libLyger:set_sub(sub, sel)
|
||||
|
||||
--Read in first line
|
||||
local first_line = libLyger.lines[sel[1]]
|
||||
|
||||
--Read in the clip
|
||||
--No need to double check, since the validate function ensures this
|
||||
local _,_,sclip1,sclip2,sclip3,sclip4=
|
||||
first_line.text:find("\\clip%(([%d%.%-]*),([%d%.%-]*),([%d%.%-]*),([%d%.%-]*)%)")
|
||||
sclip1=tonumber(sclip1)
|
||||
sclip2=tonumber(sclip2)
|
||||
sclip3=tonumber(sclip3)
|
||||
sclip4=tonumber(sclip4)
|
||||
|
||||
|
||||
--Get position
|
||||
sx, sy = libLyger:get_pos(first_line)
|
||||
|
||||
for i=2,#sel,1 do
|
||||
--Read the line
|
||||
this_line = libLyger.lines[sel[i]]
|
||||
|
||||
--Get its position
|
||||
tx, ty = libLyger:get_pos(this_line)
|
||||
|
||||
--Deltas
|
||||
d_x,d_y=tx-sx,ty-sy
|
||||
|
||||
--Remove any existing rectangular clip
|
||||
this_line.text=this_line.text:gsub("\\clip%(([%d%.%-]*),([%d%.%-]*),([%d%.%-]*),([%d%.%-]*)%)","")
|
||||
|
||||
--Add clip
|
||||
this_line.text=string.format("{\\clip(%d,%d,%d,%d)}",
|
||||
sclip1+d_x,sclip2+d_y,sclip3+d_x,sclip4+d_y)..this_line.text
|
||||
this_line.text=this_line.text:gsub("}{","")
|
||||
sub[sel[i]]=this_line
|
||||
end
|
||||
|
||||
aegisub.set_undo_point(script_name)
|
||||
end
|
||||
|
||||
--Make sure the first line contains a rectangular clip
|
||||
function validate_clip_shift(sub,sel)
|
||||
return #sel>1 and
|
||||
sub[sel[1]].text:find("\\clip%(([%d%.%-]*),([%d%.%-]*),([%d%.%-]*),([%d%.%-]*)%)")~=nil
|
||||
end
|
||||
|
||||
rec:registerMacro(clip_shift, validate_clip_shift)
|
227
.aegisub/automation/autoload/lyger.GradientByChar.lua
Normal file
227
.aegisub/automation/autoload/lyger.GradientByChar.lua
Normal file
|
@ -0,0 +1,227 @@
|
|||
--[[
|
||||
==README==
|
||||
|
||||
No GUI, pretty straightforward.
|
||||
|
||||
For example, to make a line bend in an arc and transition from blue to red, do this:
|
||||
|
||||
{\frz10\c&HFF0000&}This is a line of tex{\frz350\c&H0000FF&}t
|
||||
|
||||
Run the automation and it'll add a tag before each character to make the rotation and color
|
||||
transition change smoothly across the line.
|
||||
|
||||
Rotations are locked to less than 180 degree rotations. If you want a bend of more than 180,
|
||||
then split it up into multiple rotations of less than 180 each. This script is meant for
|
||||
convenience above all, so it runs with a single button press and no time-consuming options menu.
|
||||
|
||||
]]--
|
||||
|
||||
script_name = "Gradient by character"
|
||||
script_description = "Smoothly transforms tags across your line, by character."
|
||||
script_version = "1.3.1"
|
||||
script_author = "lyger"
|
||||
script_namespace = "lyger.GradientByChar"
|
||||
|
||||
local DependencyControl = require("l0.DependencyControl")
|
||||
local rec = DependencyControl{
|
||||
feed = "https://raw.githubusercontent.com/TypesettingTools/lyger-Aegisub-Scripts/master/DependencyControl.json",
|
||||
{
|
||||
{"lyger.LibLyger", version = "2.0.0", url = "http://github.com/TypesettingTools/lyger-Aegisub-Scripts"},
|
||||
"aegisub.util", "aegisub.re"
|
||||
}
|
||||
}
|
||||
local LibLyger, util, re = rec:requireModules()
|
||||
local libLyger = LibLyger()
|
||||
|
||||
function grad_char(sub,sel)
|
||||
libLyger:set_sub(sub, sel)
|
||||
|
||||
for si,li in ipairs(sel) do
|
||||
--Read in the line
|
||||
this_line=libLyger.lines[li]
|
||||
|
||||
--Make sure line starts with tags
|
||||
if this_line.text:find("^{")==nil then this_line.text="{}"..this_line.text end
|
||||
|
||||
--Turn all \1c tags into \c tags, just for convenience
|
||||
this_line.text=this_line.text:gsub("\\1c","\\c")
|
||||
|
||||
--Make line table
|
||||
this_table={}
|
||||
x=1
|
||||
for thistag,thistext in this_line.text:gsub("}","}\t"):gmatch("({[^{}]*})([^{}]*)") do
|
||||
this_table[x]={tag=thistag,text=thistext:gsub("\t","")}
|
||||
x=x+1
|
||||
end
|
||||
|
||||
if #this_table<2 then
|
||||
aegisub.log("There must be more than one tag block in the line!")
|
||||
return
|
||||
end
|
||||
|
||||
--Transform these tags
|
||||
transform_tags={
|
||||
"c","2c","3c","4c",
|
||||
"alpha","1a","2a","3a",
|
||||
"fscx","fscy","fax","fay",
|
||||
"frx","fry","frz",
|
||||
"fs","fsp",
|
||||
"bord","shad",
|
||||
"xbord","ybord","xshad","yshad",
|
||||
"blur","be"
|
||||
}
|
||||
|
||||
--Make state table
|
||||
this_state = LibLyger.make_state_table(this_table,transform_tags)
|
||||
|
||||
--Style lookup
|
||||
this_style = libLyger:style_lookup(this_line)
|
||||
|
||||
--Running record of the state of the line
|
||||
current_state={}
|
||||
|
||||
--Outer control loop
|
||||
for i=2,#this_table,1 do
|
||||
--Update current state
|
||||
for ctag,cval in pairs(this_state[i-1]) do
|
||||
current_state[ctag]=cval
|
||||
end
|
||||
|
||||
--Stores state of each character, to prevent redundant tags
|
||||
char_state=util.deep_copy(current_state)
|
||||
|
||||
--Local function for interpolation
|
||||
local function handle_interpolation(factor,tag,sval,eval)
|
||||
local param_type, ivalue = libLyger.param_type, ""
|
||||
--Handle differently depending on the type of tag
|
||||
if param_type[tag]=="alpha" then
|
||||
ivalue=interpolate_alpha(factor,sval,eval)
|
||||
elseif param_type[tag]=="color" then
|
||||
ivalue=interpolate_color(factor,sval,eval)
|
||||
elseif param_type[tag]=="angle" then
|
||||
nstart=tonumber(sval)
|
||||
nend=tonumber(eval)
|
||||
|
||||
--Use "Rotate in shortest direction" by default
|
||||
nstart=nstart%360
|
||||
nend=nend%360
|
||||
ndelta=nend-nstart
|
||||
if math.abs(ndelta)>180 then nstart=nstart+(ndelta*360)/math.abs(ndelta) end
|
||||
|
||||
--Interpolate
|
||||
nvalue=interpolate(factor,nstart,nend)
|
||||
if nvalue<0 then nvalue=nvalue+360 end
|
||||
|
||||
--Convert to string
|
||||
ivalue=libLyger.float2str(nvalue)
|
||||
|
||||
elseif param_type[tag]=="number" then
|
||||
nstart=tonumber(sval)
|
||||
nend=tonumber(eval)
|
||||
|
||||
--Interpolate and convert to string
|
||||
ivalue=libLyger.float2str(interpolate(factor,nstart,nend))
|
||||
end
|
||||
return ivalue
|
||||
end
|
||||
|
||||
local ttext=this_table[i-1].text
|
||||
|
||||
if ttext:len()>0 then
|
||||
|
||||
--Rebuilt text
|
||||
local rtext=""
|
||||
|
||||
--Skip the first character
|
||||
local first=true
|
||||
|
||||
--Starting values
|
||||
idx=1
|
||||
|
||||
matches=re.find(ttext,'\\\\[Nh]|\\X')
|
||||
|
||||
total=#matches
|
||||
|
||||
for _,match in ipairs(matches) do
|
||||
|
||||
ch=match.str
|
||||
|
||||
if not first then
|
||||
--Interpolation factor
|
||||
factor=idx/total
|
||||
|
||||
idx=idx+1
|
||||
|
||||
--Do nothing if the character is a space
|
||||
if ch:find("%s")~=nil then
|
||||
rtext=rtext..ch
|
||||
else
|
||||
|
||||
--The tags in and out of the time statement
|
||||
local non_time_tags=""
|
||||
|
||||
--Go through all the state tags in this tag block
|
||||
for ttag,tparam in pairs(this_state[i]) do
|
||||
--Figure out the starting state of the param
|
||||
local sparam=current_state[ttag]
|
||||
if sparam==nil then sparam=this_style[ttag] end
|
||||
if type(sparam)~="number" then sparam=sparam:gsub("%)","") end--Just in case a \t tag snuck in
|
||||
|
||||
--Prevent redundancy
|
||||
if sparam~=tparam then
|
||||
--The string version of the interpolated parameter
|
||||
local iparam=handle_interpolation(factor,ttag,sparam,tparam)
|
||||
|
||||
if iparam~=tostring(char_state[ttag]) then
|
||||
non_time_tags=non_time_tags.."\\"..ttag..iparam
|
||||
char_state[ttag]=iparam
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if non_time_tags:len() < 1 then
|
||||
--If no tags were added, do nothing
|
||||
rtext=rtext..ch
|
||||
else
|
||||
--The final tag, with a star to indicate it was added through interpolation
|
||||
rtext=rtext.."{*"..non_time_tags.."}"..ch
|
||||
end
|
||||
|
||||
end
|
||||
else
|
||||
rtext=rtext..ch
|
||||
end
|
||||
first=false
|
||||
end
|
||||
|
||||
this_table[i-1].text=rtext
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
rebuilt_text=""
|
||||
|
||||
for i,val in pairs(this_table) do
|
||||
rebuilt_text=rebuilt_text..val.tag..val.text
|
||||
end
|
||||
this_line.text=rebuilt_text
|
||||
sub[li]=this_line
|
||||
end
|
||||
|
||||
aegisub.set_undo_point(script_name)
|
||||
end
|
||||
|
||||
function remove_grad_char(sub,sel)
|
||||
for si,li in ipairs(sel) do
|
||||
this_line=sub[li]
|
||||
this_line.text=this_line.text:gsub("{%*[^{}]*}","")
|
||||
sub[li]=this_line
|
||||
end
|
||||
end
|
||||
|
||||
-- Register the macro
|
||||
rec:registerMacros{
|
||||
{"Apply Gradient", script_description, grad_char},
|
||||
{"Remove Gradient", "Removes gradient generated by #{script_name}", remove_grad_char}
|
||||
}
|
1196
.aegisub/automation/autoload/ua.MultiCopy.lua
Normal file
1196
.aegisub/automation/autoload/ua.MultiCopy.lua
Normal file
File diff suppressed because it is too large
Load diff
1243
.aegisub/automation/autoload/ua.NecrosCopy.lua
Normal file
1243
.aegisub/automation/autoload/ua.NecrosCopy.lua
Normal file
File diff suppressed because it is too large
Load diff
3493
.aegisub/automation/autoload/ua.Relocator.lua
Normal file
3493
.aegisub/automation/autoload/ua.Relocator.lua
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue