Skip to content

Commit

Permalink
Perspective, Resample: Move more logic to Perspective module
Browse files Browse the repository at this point in the history
  • Loading branch information
arch1t3cht committed Nov 24, 2023
1 parent 85ade99 commit b725cc2
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 72 deletions.
14 changes: 13 additions & 1 deletion DependencyControl.json
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@
"moduleName": "arch.Perspective",
"name": "Perspective",
"url": "https://github.com/TypesettingTools/arch1t3cht-Aegisub-Scripts",
"version": "0.2.5"
"version": "1.0.0"
}
]
}
Expand Down Expand Up @@ -268,6 +268,12 @@
"Add proper handling for drawings",
"Bump Perspective dependency to fix resampling in cases involving 180 degree rotations",
"Add warning messages for cases where the script is likely to break"
],
"2.0.0": [
"Add support for nofax \\org mode"
],
"2.1.0": [
"Move some more logic over to the Perspective module"
]
}
},
Expand Down Expand Up @@ -553,6 +559,12 @@
],
"0.2.4": [
"Fix the tagsFromQuad computation when 180 degree rotations are involved"
],
"0.2.5": [
"Add support for a nofax org mode"
],
"1.0.0": [
"Add prepareForPerspective function that wraps the remaining logic needed for general perspective computations with ASSFoundation"
]
}
}
Expand Down
100 changes: 30 additions & 70 deletions macros/arch.Resample.moon
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ export script_name = "Resample Perspective"
export script_description = "Apply after resampling a script in Aegisub to fix any lines with 3D rotations."
export script_author = "arch1t3cht"
export script_namespace = "arch.Resample"
export script_version = "2.0.0"
export script_version = "2.1.0"

DependencyControl = require "l0.DependencyControl"
dep = DependencyControl{
Expand All @@ -14,20 +14,16 @@ dep = DependencyControl{
feed: "https://raw.githubusercontent.com/TypesettingTools/ASSFoundation/master/DependencyControl.json"},
{"arch.Math", version: "0.1.8", url: "https://github.com/TypesettingTools/arch1t3cht-Aegisub-Scripts",
feed: "https://raw.githubusercontent.com/TypesettingTools/arch1t3cht-Aegisub-Scripts/main/DependencyControl.json"},
{"arch.Perspective", version: "0.2.5", url: "https://github.com/TypesettingTools/arch1t3cht-Aegisub-Scripts",
{"arch.Perspective", version: "1.0.0", url: "https://github.com/TypesettingTools/arch1t3cht-Aegisub-Scripts",
feed: "https://raw.githubusercontent.com/TypesettingTools/arch1t3cht-Aegisub-Scripts/main/DependencyControl.json"},
}
}
LineCollection, ASS, AMath, APersp = dep\requireModules!
{:Matrix} = AMath
{:transformPoints, :tagsFromQuad} = APersp
{:relevantTags, :usedTags, :transformPoints, :tagsFromQuad, :prepareForPerspective} = APersp

logger = dep\getLogger!

alltags = {"shear_x", "shear_y", "scale_x", "scale_y", "angle", "angle_x", "angle_y", "origin", "position", "outline", "outline_x", "outline_y", "shadow", "shadow_x", "shadow_y"}
usedtags = {"shear_x", "shear_y", "scale_x", "scale_y", "angle", "angle_x", "angle_y", "origin", "position", "outline_x", "outline_y", "shadow_x", "shadow_y"}


resample = (ratiox, ratioy, orgmode, subs, sel) ->
anamorphic = math.max(ratiox, ratioy) / math.min(ratiox, ratioy) > 1.01

Expand All @@ -38,73 +34,37 @@ resample = (ratiox, ratioy, orgmode, subs, sel) ->
-- No perspective tags, we don't need to do anything
return if not anamorphic and #data\getTags({"angle_x", "angle_y"}) == 0

tagvals = data\getEffectiveTags(-1, true, true, true).tags
tagvals, width, height, warnings = prepareForPerspective(ASS, data)

for warn in *warnings
switch warn[1]
when "multiple_tags"
aegisub.log("Warning: Line #{line.humanizedNumber} has more than one #{warn[2]} tag! This might break resampling.\n") if warn[2] == "\\frx" or warn[2] == "\\fry"
when "transform"
aegisub.log("Warning: Line #{line.humanizedNumber} contains a #{warn[2]} tag in a transform tag! This might break resampling.\n") if warn[2] == "\\frx" or warn[2] == "\\fry"

return if not anamorphic and tagvals.angle_x.value == 0 and tagvals.angle_y.value == 0

width, height = 0, 0
has_text, has_drawing = false, false

data\callback (section) ->
if section.class == ASS.Section.Text
has_text = true
width, height = data\getTextExtents!
if section.class == ASS.Section.Drawing
has_drawing = true
bounds = section\getBounds!
width, height = bounds.w, bounds.h

if has_text and has_drawing
aegisub.log("Line #{line.humanizedNumber} has both text and drawings! Skipping.\n")
return

if has_text and (width == 0 or height == 0)
aegisub.log("Warning: Line #{line.humanizedNumber} has zero width or height!\n")

-- Width and height can be 0 for drawings
width = math.max(width, 0.01)
height = math.max(height, 0.01)

width /= (tagvals.scale_x.value / 100)
height /= (tagvals.scale_y.value / 100)

if data\getPosition().class == ASS.Tag.Move
aegisub.log("Line #{line.humanizedNumber} has \\move! Skipping.\n")
return

-- Do some checks for cases that break this script
-- These are a bit more aggressive than necessary (e.g. two tags of the same type in the same section will trigger this detection but not break resampling)
-- but I can't be bothered to be more exact. Users can run ASSWipe before resampling or something.
for tname in *alltags
if #data\getTags({tname}) >= 2
aegisub.log("Warning: Line #{line.humanizedNumber} has more than one #{ASS.tagMap[tname].overrideName} tag! This might break resampling.")

-- Assf doesn't support nested transforms so this code could be much simpler, but a) I only found that out after writing this and b) I guess I can
-- keep this code around in case it ever starts supporting them
checkTransformTags = (section, initial) ->
if not initial
for tname in *alltags
if #section\getTags({tname}) >= 1
aegisub.log("Warning: Line #{line.humanizedNumber} contains a #{ASS.tagMap[tname].overrideName} tag in a transform tag! This might break resampling.")

section\modTags {"transform"}, (tag) ->
checkTransformTags tag.tags, false
tag

checkTransformTags data, true

-- Manually enforce the relations between tags
if #data\getTags({"origin"}) == 0
tagvals.origin.x = tagvals.position.x
tagvals.origin.y = tagvals.position.y
for name in *{"outline", "shadow"}
for coord in *{"x", "y"}
cname = "#{name}_#{coord}"
if #data\getTags({cname}) == 0
tagvals[cname].value = tagvals[name].value
for warn in *warnings
switch warn[1]
when "multiple_tags"
aegisub.log("Warning: Line #{line.humanizedNumber} has more than one #{warn[2]} tag! This might break resampling.\n")
when "transform"
aegisub.log("Warning: Line #{line.humanizedNumber} contains a #{warn[2]} tag in a transform tag! This might break resampling.\n")
when "zero_size"
aegisub.log("Warning: Line #{line.humanizedNumber} has zero width or height!\n")
when "move"
aegisub.log("Line #{line.humanizedNumber} has \\move! Skipping.\n")
return
when "text_and_drawings"
aegisub.log("Line #{line.humanizedNumber} has both text and drawings! Skipping.\n")
return
else
aegisub.log("Unknown warning on line #{line.humanizedNumber}: #{warn[1]}\n")

-- Set up the tags
data\removeTags alltags
data\insertTags [ tagvals[k] for k in *usedtags ]
data\removeTags relevantTags
data\insertTags [ tagvals[k] for k in *usedTags ]

-- Revert Aegisub's resampling.
for tag in *{"position", "origin"}
Expand Down
85 changes: 84 additions & 1 deletion modules/arch/Perspective.moon
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ local amath
if haveDepCtrl
depctrl = DependencyControl {
name: "Perspective",
version: "0.2.5",
version: "1.0.0",
description: [[Math functions for dealing with perspective transformations.]],
author: "arch1t3cht",
url: "https://github.com/TypesettingTools/arch1t3cht-Aegisub-Scripts",
Expand Down Expand Up @@ -118,6 +118,86 @@ screen_z = 312.5
an_xshift = { 0, 0.5, 1, 0, 0.5, 1, 0, 0.5, 1 }
an_yshift = { 1, 1, 1, 0.5, 0.5, 0.5, 0, 0, 0 }


-- List of tags that affect perspective
relevantTags = {"fontsize", "shear_x", "shear_y", "scale_x", "scale_y", "angle", "angle_x", "angle_y", "origin", "position", "outline", "outline_x", "outline_y", "shadow", "shadow_x", "shadow_y"}

-- List of tags that are used in perspective. This is the same list as relevant_tags except for outline and shadow, since the single-coordinate versions of those tags are used instead
usedTags = {"fontsize", "shear_x", "shear_y", "scale_x", "scale_y", "angle", "angle_x", "angle_y", "origin", "position", "outline_x", "outline_y", "shadow_x", "shadow_y"}

-- Takes an ASSFoundation LineContents object and returns its effective tags, but preprocessed
-- for perspective handling. This includes inforcing the relations between \org and \pos as
-- well as those between the various transform tags, as well as warning when the line has tags
-- that would break perspective calulations (\move, certain \t transformations, multiple tag sections, etc).
-- Also needs the ASSFoundation library to be given in its first parameter. This is to prevent this library
-- from depending on ASSFoundation.
-- Returns:
-- - the effective tags table
-- - the line's width (not scaled with \fscx)
-- - the line's height (not scaled with \fscy)
-- - a table of warnings about possibly problematic tags
-- -> each warning is of the form {warning, details} where details may be nil depending on the warning.
prepareForPerspective = (ASS, data) ->
tagvals = data\getEffectiveTags(-1, true, true, true).tags
return if not anamorphic and tagvals.angle_x.value == 0 and tagvals.angle_y.value == 0

width, height = 0, 0
has_text, has_drawing = false, false

data\callback (section) ->
if section.class == ASS.Section.Text
has_text = true
width, height = data\getTextExtents!
if section.class == ASS.Section.Drawing
has_drawing = true
bounds = section\getBounds!
width, height = bounds.w, bounds.h

warnings = {}

table.insert(warnings, {"text_and_drawings"}) if has_text and has_drawing
table.insert(warnings, {"zero_size"}) if has_text and (width == 0 or height == 0)
table.insert(warnings, {"move"}) if data\getPosition().class == ASS.Tag.Move

-- Width and height can be 0 for drawings
width = math.max(width, 0.01)
height = math.max(height, 0.01)

width /= (tagvals.scale_x.value / 100)
height /= (tagvals.scale_y.value / 100)

-- Do some checks for cases that break this script
-- These are a bit more aggressive than necessary (e.g. two tags of the same type in the same section will trigger this detection but not break resampling)
-- but I can't be bothered to be more exact. Users can run ASSWipe before resampling or something.
for tname in *relevantTags
table.insert(warnings, {"multiple_tags", ASS.tagMap[tname].overrideName}) if #data\getTags({tname}) >= 2

-- Assf doesn't support nested transforms so this code could be much simpler, but a) I only found that out after writing this and b) I guess I can
-- keep this code around in case it ever starts supporting them
checkTransformTags = (section, initial) ->
if not initial
for tname in *relevantTags
table.insert(warnings, {"transform", ASS.tagMap[tname].overrideName}) if #section\getTags({tname}) >= 1

section\modTags {"transform"}, (tag) ->
checkTransformTags tag.tags, false
tag

checkTransformTags data, true

-- Manually enforce the relations between tags
if #data\getTags({"origin"}) == 0
tagvals.origin.x = tagvals.position.x
tagvals.origin.y = tagvals.position.y
for name in *{"outline", "shadow"}
for coord in *{"x", "y"}
cname = "#{name}_#{coord}"
if #data\getTags({cname}) == 0
tagvals[cname].value = tagvals[name].value

return tagvals, width, height, warnings


-- Transforms the given list of points in a relative coordinate system according to the given .ass tags.
-- If no list of points is given, a rectangle with the given dimensions is used.
-- The width and height parameters should contain the raw dimensions of the line to be transformed. These are used for alignment.
Expand Down Expand Up @@ -293,6 +373,9 @@ tagsFromQuad = (t, quad, width, height, orgMode=0) ->

lib = {
:Quad,
:relevantTags,
:usedTags,
:prepareForPerspective
:transformPoints,
:tagsFromQuad,
}
Expand Down

0 comments on commit b725cc2

Please sign in to comment.