230 lines
8.4 KiB
Plaintext
230 lines
8.4 KiB
Plaintext
!! Everything here is untested and best check for errors in the calculations !!
|
||
|
||
Choose \fay, \fax, \fscx and \fscy to create a (almost) general 2D-transform-matrix
|
||
(Rotations are sometimes also used)
|
||
|
||
(ASS Transform order: combined faxy, scale, frz, frx, fry)
|
||
|
||
If shearOrigin == scaleOrigin == rotationOrigin == (0,0)^T
|
||
(which iirc is true for drawings with \org(0,0)?),
|
||
and there are no run splits, and if assuming \frz0 for now,
|
||
ASS will effectively use the following transform matrix
|
||
|
||
| scale_x 0 | | 1 fax |
|
||
| | * | |
|
||
| 0 scale_y | | fay 1 |
|
||
|
||
|
||
| scale_x scale_x fax |
|
||
= | |
|
||
| scale_y fay scale_y |
|
||
|
||
|
||
Let's assume that's true.
|
||
Then given an arbitrary transform matrix M,
|
||
with known coefficients a, b, c, d \in \R
|
||
| a b |
|
||
M = | |
|
||
| c d |
|
||
|
||
we get:
|
||
|
||
I: scale_x = a
|
||
II: scale_y = d
|
||
III: scale_x fax = b
|
||
IV: scale_y fay = c
|
||
|
||
If (a = b = 0) or (c = d = 0), then scale_x = 0
|
||
or scale_y = 0 and all other values can be set to zero
|
||
as the event vanishes anyway.
|
||
|
||
If (a = 0 and b != 0) or (c != 0 and d = 0), then
|
||
we can't solve this as is directly. Ignore this
|
||
case for now, we'll later describe how to adapt the solution for this.
|
||
|
||
If scale_x = a != 0 != d = scale_y, it follows that
|
||
III => fax = b / scale_x
|
||
IV => fay = c / scale_y
|
||
|
||
But even in this case, there's one issue:
|
||
scale_x, scale_y may be negative,
|
||
but ASS only allows positive scale values.
|
||
|
||
To remedy this we'll use 3D rotations to emulate negative scale.
|
||
It is easy to see, that by themselves a
|
||
rotation around x by π, is equivalent to scale_y = -1 and
|
||
a rotation around y by π is equivalent to scale_x = -1.
|
||
If we use \fscx and \fscy to scale by the respective absolute values
|
||
and immediately follow this up by a “-1”-scale if needed the result
|
||
will be equivalent to a scale by the proper value.
|
||
In general this isn't so straightforward as ASS always applies rotations
|
||
in the order of z, x, y, but we chose rot_z = 0, resulting in it being a
|
||
dismissable identity operation.
|
||
|
||
If scale_x is negative, pass the absolute value to \fscx and add \fry180.
|
||
If scale_y is negative, pass the absolute value to \fscy and add \frx180.
|
||
|
||
ASS-3D-rotation matrices:
|
||
|
||
| 1 0 0 |
|
||
| |
|
||
Rx = | 0 cos(rot_x) sin(rot_x) |
|
||
| |
|
||
| 0 sin(rot_x) -cos(rot_x) |
|
||
|
||
|
||
| cos(rot_y) 0 sin(rot_y) |
|
||
| |
|
||
Ry = | 0 1 0 |
|
||
| |
|
||
| sin(rot_y) 0 -cos(rot_y) |
|
||
|
||
|
||
| cos(rot_z) -sin(rot_z) 0 |
|
||
| |
|
||
Rz = | sin(rot_z) cos(rot_z) 0 | (for choice of rot_z=0 identity matrix)
|
||
| |
|
||
| 0 0 1 |
|
||
|
||
(from: https://sourceforge.net/p/guliverkli2/code/HEAD/tree/src/subtitles/RTS.cpp#l148)
|
||
|
||
|
||
Using sgn_x, sgn_y \in {-1, 1} as the sign of the respective scales
|
||
and sc_x, sc_y \in { x >= 0 | x \in \R } as the absolute scale values,
|
||
all in all we get the following tags
|
||
|
||
\fry(-360*(sgn_x-1)) \frx(-360/(sgn_y-1)) \fscx(sc_x) \fscy(sc_y) \fax(fax) \fay(fay) \org(0,0)
|
||
|
||
which results in the ASS transform matrix:
|
||
|
||
|
||
| sgn_x 0 0 | | 1 0 0 | | sc_x sc_x fax 0 |
|
||
| | | | | |
|
||
| 0 1 0 | * | 0 sgn_y 0 | * | sc_y fay sc_y 0 |
|
||
| | | | | |
|
||
| 0 0 -sgn_x | | 0 0 -sgn_y | | 0 0 1 |
|
||
|
||
|
||
| sgn_x 0 0 | | sc_x sc_x fax 0 |
|
||
| | | |
|
||
= | 0 sgn_y 0 | * | sc_y fay sc_y 0 |
|
||
| | | |
|
||
| 0 0 sgn_x*sgn_y | | 0 0 1 |
|
||
|
||
|
||
| sgn_x*sc_x sgn_x*sc_x fax 0 |
|
||
| |
|
||
= | sgn_y*sc_y fay sgn_y*sc_y 0 |
|
||
| |
|
||
| 0 0 sgn_x*sgn_y |
|
||
|
||
|
||
using sgn_x*sc_x = scale_x and sgn_y*sc_y = scale_y
|
||
|
||
|
||
| scale_x scale_x fax 0 |
|
||
| |
|
||
= | scale_y fay scale_y 0 |
|
||
| |
|
||
| 0 0 sgn_x*sgn_y |
|
||
|
||
|
||
| a b 0 |
|
||
| |
|
||
= | c d 0 |
|
||
| |
|
||
| 0 0 sgn_x*sgn_y |
|
||
|
||
|
||
Apart from flipping the sign of the z-coordinates when sgn_x != sgn_y
|
||
this is exactly the matrix we wanted.
|
||
Since 2D-glyphs and -drawings are initially placed at z=0,
|
||
this additional sign-flip is of no concern.
|
||
|
||
---
|
||
|
||
Now coming back to the case of (a = 0 and b != 0) or (c != 0 and d = 0):
|
||
|
||
To be able to solve this we also need to apply a z-Rotation:
|
||
|
||
1) If in each column there's at least one non-zero entry or one row is all zero
|
||
(together with case condition the latter means only b xor c is non-zero):
|
||
Use \frz90, being a row swap with a sign flip:
|
||
|
||
| 0 -1 |
|
||
\frz90 = | |
|
||
| 1 0 |
|
||
|
||
So our transform-matrix equation becomes:
|
||
|
||
| -scale_y fay -scale_y | | a b |
|
||
| | = | |
|
||
| scale_x scale_x fax | | c d |
|
||
|
||
Which can be solved as before for signed scales.
|
||
The additon of a z-Rotation means our "scale sign"-roations
|
||
no longer immediately follow the actual scale.
|
||
To compensate, use \frx for sgn_x and \fry for sgn_y
|
||
(roles reversed from before)
|
||
|
||
2) Otherwise:
|
||
(If one column is zero and the other one has only non-zero entries)
|
||
|
||
2.1) a = 0 = c and b ≠ 0 ≠ d
|
||
|
||
| scale_x*cos(rz) - scale_y*fay*sin(rz) scale_x*fax*cos(rz) - scale_y*sin(rz) | | 0 b |
|
||
| | = | |
|
||
| scale_x*sin(rz) + scale_y*fay*cos(rz) scale_x*fax*sin(rz) + scale_y*cos(rz) | | 0 d |
|
||
|
||
I: scale_x*cos(rz) - scale_y*fay*sin(rz) = 0
|
||
II: scale_x*sin(rz) + scale_y*fay*cos(rz) = 0
|
||
III: scale_x*fax*cos(rz) - scale_y*sin(rz) = b
|
||
IV: scale_x*fax*sin(rz) + scale_y*cos(rz) = d
|
||
|
||
I² + II² ⇒ scale_x^2 = - scale_y^2 * fay^2 ⇒ scale_x = 0 and (scale_y = 0 or fay = 0)
|
||
|
||
III' : -scale_y*sin(rz) = b
|
||
IV' : scale_y*cos(rz) = d
|
||
|
||
⇒ since b ≠ 0 ≠ d ⇒ scale_y ≠ 0 ⇒ fay = 0
|
||
|
||
⇒ ||scale_y|| = sqrt(b^2 + d^2)
|
||
⇒ rz = tan⁻¹(-b/d)
|
||
Note: The conventional tan⁻¹ definition always returns positive cosine.
|
||
Thus we can always coose the positive solution for scale_y
|
||
and instead add π to rot_z if d < 0
|
||
|
||
Since scale_y > 0 and scale_x = 0 we do not need \frx and \fry to simulate a negative scale.
|
||
|
||
2.2) b = 0 = d and a ≠ 0 ≠ c
|
||
|
||
| scale_x*cos(rz) - scale_y*fay*sin(rz) scale_x*fax*cos(rz) - scale_y*sin(rz) | | a 0 |
|
||
| | = | |
|
||
| scale_x*sin(rz) + scale_y*fay*cos(rz) scale_x*fax*sin(rz) + scale_y*cos(rz) | | c 0 |
|
||
|
||
|
||
I: scale_x*fax*cos(rz) - scale_y*sin(rz) = 0
|
||
II: scale_x*fax*sin(rz) + scale_y*cos(rz) = 0
|
||
III: scale_x*cos(rz) - scale_y*fay*sin(rz) = a
|
||
IV: scale_x*sin(rz) + scale_y*fay*cos(rz) = b
|
||
|
||
I² + II² ⇒ scale_y^2 = - scale_x^2 * fax^2 ⇒ scale_y = 0 and (scale_x = 0 or fax = 0)
|
||
|
||
III' : scale_x*cos(rz) = a
|
||
IV' : scale_x*sin(rz) = c
|
||
|
||
⇒ since a ≠ 0 ≠ c ⇒ scale_x ≠ 0 ⇒ fax = 0
|
||
|
||
⇒ ||scale_x|| = sqrt(a^2 + c^2)
|
||
⇒ rz = tan⁻¹(c/a)
|
||
Note: The conventional tan⁻¹ definition always returns positive cosine.
|
||
Thus we can always coose the positive solution for scale_x
|
||
and instead add π to rot_z if a < 0
|
||
|
||
Since scale_x > 0 and scale_y = 0 we do not need \frx and \fry to simulate a negative scale.
|
||
|
||
In both 2.1) and 2.2) we get one dimension scaled to zero, and – if I didin't miss something –
|
||
no contradictions in the equations. Meaning the event actually vanishes and is visually identical
|
||
to choosing all parameters zero. If interpolations are involved having the mathematically accurate
|
||
transform matrix might still be good though.
|