[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Gnash-dev] Re:Matrix math (strk)
From: |
Mark Voorhies |
Subject: |
[Gnash-dev] Re:Matrix math (strk) |
Date: |
Tue, 2 Sep 2008 23:23:01 -0700 |
User-agent: |
KMail/1.9.6 (enterprise 0.20070907.709405) |
> Message: 1
> Date: Sun, 31 Aug 2008 21:50:36 +0800
> From: "zou lunkai" <address@hidden>
> Subject: [Gnash-dev] Re:Matrix math (strk)
> To: strk <address@hidden>
> Cc: gnash-dev <address@hidden>
> Message-ID:
> <address@hidden>
> Content-Type: text/plain; charset=ISO-8859-1
>
> > So we now cache matrix parameters in the character class
> > and recompute the matrix from all scales and a rotation
> > when needed.
> >
> > Question now is: does it still make sense to have
> > matrix methods: set_x_scale, set_y_scale, set_rotation ?
> >
> > The core only uses set_scale_rotation now...
> >
> > Only user of the others is the testcase, which fails.
> > It seems to me that there's no way to tell original parameters
> > from the matrix itself, while the final transformation might
> > be good enough.
In order to decompose the matrix into the original parameters, you _do_ need
to specify the order of application. E.g., the current get_* functions
appear to assume "scale then rotate then translate" with no shearing. I'm
not sure if this assumption holds for the expected uses of the get_*
functions.
>
> > See the testcase testsuite/libcore.all/MatrixTest.cpp
> > There you see how setting scale to -scale fails to remember
> > scale got negative and instead introduces a rotation of 180 degrees.
I think that this is the relevant code (from trunk/libcore/matrix.cpp):
double
matrix::get_x_scale() const
{
return sqrt(((double)sx * sx + (double)shx * shx)) / 65536.0;
}
double
matrix::get_y_scale() const
{
return sqrt(((double)sy * sy + (double)shy * shy)) / 65536.0;
}
double
matrix::get_rotation() const
{
return atan2(shx, sx); // more successes in misc-ming.all/matrix_test.c
if (determinant() < 0)
{
// TODO: check this.
return atan2(shx, -sx);
}
else
{
return atan2(shx, sx);
}
}
Note the changes relative to the AGG code. These are the formulas that you
arrive at if you set up
[sx shx, sy shy] =
[cos(rot) -sin(rot), sin(rot) cos(rot)]*[x_scale 0, 0 y_scale]
and solve for rot, x_scale, and y_scale assuming no reflection.
I think that the unreachable code in get_rotation() is correctly checking for
a reflection by checking for a negative determinant (c.f.
http://planetmath.org/encyclopedia/DecompositionOfOrthogonalOperatorsAsRotationsAndReflections.html
)
but handling the reflecting case by reversing the rotation is incorrect (In
general, rotating backwards will give a reflection only for symmetric
shapes).
I think that pushing the reflection into sx gives the expected behavior as
long as the sx != 0 (choosing sx rather than sy is arbitrary).
For the reflection case, atan2 returns the wrong sign (the logic for assigning
quadrants assumes only rotation), so it is still necessary to do the sign
correction in get_rotation:
E.g.
double
matrix::get_x_scale() const
{
if(determinant() < 0)
{
// Capture reflection in x-scale
return -sqrt(((double)sx * sx + (double)shx * shx)) / 65536.0;
}
else
{
return sqrt(((double)sx * sx + (double)shx * shx)) / 65536.0;
}
}
double
matrix::get_y_scale() const
{
return sqrt(((double)sy * sy + (double)shy * shy)) / 65536.0;
}
double
matrix::get_rotation() const
{
if (determinant() < 0)
{
// Fix sign of rotation for reflection case.
// TODO: check this.
return atan2(shx, -sx);
}
else
{
return atan2(shx, sx);
}
}
I can't build gnash locally, so I haven't tested this, but a bit of testing of
this decomposition with a mock-up of the matrix class in Python does appear
to work.
(Of course, if the order of operations is unknown for the general use case,
then you're probably still better off using the cached values)
--Mark
- [Gnash-dev] Re:Matrix math (strk),
Mark Voorhies <=