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.
|