get skew or rotation value from affine transformation matrix - actionscript-3

I'm trying to get the skew values out of a transformation matrix in a flash movie clip. The transformation matrix is represented by
a b tx
c d ty
0 0 1
I have no information on what kind of transformation is performed and which comes first. I do know that in flash, you may only rotate OR skew a movie clip (correct me if I am wrong). I can get scale values from scaleX and scaleY properties of the movie clip. I believe translation does not quite matter i can just equate tx and ty to zero.
so my question has 2 parts. How do I determine if a skew or a rotation had been applied, and how do I get the respective values?

The 2D rotation matrix is
cos(theta) -sin(theta)
sin(theta) cos(theta)
so if you have no scaling or shear applied,
a = d
and
c = -b
and the angle of rotation is
theta = asin(c) = acos(a)
If you've got scaling applied and can recover the scaling factors sx and sy, just divide the first row by sx and the second by sy in your original transformation matrix and then recover the rotation angle as above.
If you've got a shear (skew) applied anywhere in there, I'm with the previous commenters, it might not be possible except in very limited cases (such as shear in only one known direction at a time and in a known order).

You need to do a polar decomposition. This Wikipedia article explains how it works:
http://en.wikipedia.org/wiki/Polar_decomposition
Here is the code I wrote for my own program using the OpenCV library.
const double PI = 3.141592653;
cv::Mat rotationOutput = cv::Mat::zeros(warp00.size(),CV_64F);
cv::Mat_<double>::iterator rotIter = rotationOutput.begin<double>();
cv::Mat_<double>::iterator warp00Iter = warp00.begin<double>();
cv::Mat_<double>::iterator warp01Iter = warp01.begin<double>();
cv::Mat_<double>::iterator warp10Iter = warp10.begin<double>();
cv::Mat_<double>::iterator warp11Iter = warp11.begin<double>();
for(; warp00Iter != warp00.end<double>(); ++warp00Iter, ++warp01Iter, ++warp10Iter,
++warp11Iter, ++rotIter){
cv::Matx22d fMatrix(*warp00Iter,*warp01Iter, *warp10Iter, *warp11Iter);
cv::Matx22d cMatrix;
cv::Matx22d cMatSqrt(0.,0.,0.,0.);
cv::mulTransposed(fMatrix, cMatrix, true);
cv::Matx21d eigenVals;
cv::Matx22d eigenVecs;
if((cMatrix(0,0) !=0.) && (cMatrix(1,1) !=0.)){
if(cv::eigen(cMatrix,true,eigenVals,eigenVecs)){
cMatSqrt = eigenVecs.t()*
cv::Matx22d(sqrt(eigenVals(0,0)),0.,0.,sqrt(eigenVals(1,0)))*eigenVecs;
}
}
cv::Matx22d rMat = fMatrix*cMatSqrt.inv();
*rotIter = atan(rMat(1,0)/rMat(0,0));
}
warp00, warp01, warp10 and warp11 contains the first 4 params of the affine transform (translation params warp02 and warp12 are not needed). IN your case it would be a,b,c,d.
You'll notice in the wikipedia article that you need to compute the square root of a matrix. The only way to do so is by computing the eigen values, then compute their square roots and rotate the diagonal matrix back to the original coordinate system.
It's complicated, but it is the only way to compute the rotations when you have an affine transform.
In my case, I only cared about the rotations, so my code won't give you the skew.

The term for this is matrix decomposition. Here is a solution that includes skew as described by Frédéric Wang.
Works when transforms are applied in this order: skew, scale, rotate, translate.
function decompose_2d_matrix(mat) {
var a = mat[0];
var b = mat[1];
var c = mat[2];
var d = mat[3];
var e = mat[4];
var f = mat[5];
var delta = a * d - b * c;
let result = {
translation: [e, f],
rotation: 0,
scale: [0, 0],
skew: [0, 0],
};
// Apply the QR-like decomposition.
if (a != 0 || b != 0) {
var r = Math.sqrt(a * a + b * b);
result.rotation = b > 0 ? Math.acos(a / r) : -Math.acos(a / r);
result.scale = [r, delta / r];
result.skew = [Math.atan((a * c + b * d) / (r * r)), 0];
} else if (c != 0 || d != 0) {
var s = Math.sqrt(c * c + d * d);
result.rotation =
Math.PI / 2 - (d > 0 ? Math.acos(-c / s) : -Math.acos(c / s));
result.scale = [delta / s, s];
result.skew = [0, Math.atan((a * c + b * d) / (s * s))];
} else {
// a = b = c = d = 0
}
return result;
}

First, you can do both skew and rotate, but you have to select the order first. A skew matrix is explained here, to add a skew matrix to a transformation you create a new matrix and do yourTransformMatrix.concat(skewMatrix);
I can't currently say if you can retrieve values for transformation in terms of "rotation angle", "skew_X angle", "skew_Y angle", "translation_X","translation_Y", this in general is a nonlinear equation system which might not have a solution for a specific matrix.

Related

Surfaces with different colormaps

How can multiple surfaces be plotted on the axes but surfaces uses a different colormap?.
Using colormap("...") changes it for the entire figure, not just a single surface.
Thanks
Do You mean on same axes?
I haven't found a function that does this directly. But it is possible to pass the desired colors in the surf function.
Way I found:
Convert the data to a 0-1 scale and then convert to the desired colormap.
Example with hot and jet colormaps:
tx = ty = linspace (-8, 8, 41)';
[xx, yy] = meshgrid (tx, ty);
r = sqrt (xx .^ 2 + yy .^ 2) + eps;
tz = sin (r) ./ r ;
function normalized = normalize_01(data)
data_min = min(min(data))
data_max = max(max(data))
normalized = (data - data_min)/(data_max - data_min)
endfunction
function rgb = data2rgb(data, color_bits, cmap)
grays = normalize_01(data)
indexes = gray2ind(grays, color_bits)
rgb = ind2rgb(indexes, cmap)
endfunction
color_bits = 128
cmap_1 = hot(color_bits)
rgb_1 = data2rgb(tz, color_bits, cmap_1)
surf(tx, ty, tz, rgb_1)
hold on
cmap_2 = jet(color_bits)
rgb_2 = data2rgb(tz+3, color_bits, cmap_2)
surf(tx, ty, tz+3, rgb_2)
But if you also need a colorbar, this way might not be useful. Unless you find a way to manually add two colorbar like I did with the cmap.

Is there a limit in the number of degrees of freedom with the lm_feasible algorithm? If so, what is the limit?

I am developing a finite element software that minimizes the energy of a mechanical structure. Using octave and its optim package, I run into a strange issue: The lm_feasible algorithm doesn't calculate at all when I use more than 300 degrees of freedom (DoF). Another algorithm (sqp) performs the calculation but doesn't work well when I complexify the structure and are out of my test case.
Is there a limit in the number of DoF with lm_feasible algorithm?
If so, how many DoF are maximally possible?
To give an overview and general idea of how the code works:
[x,y] = geometryGenerator()
U = zeros(lenght(x)*2,1);
U(1:2:end-1) = x;
U(2:2:end) = y;
%Non geometric argument are not optimised, and fixed during calculation
fct =#(U)complexFunctionOfEnergyIWrap(U(1:2:end-1),U(2:2:end), variousMaterialPropertiesAndOtherArgs)
para = optimset("f_equc_idx",contEq,"lb",lb,"ub",ub,"objf_grad",dEne,"objf_hessian",d2Ene,"MaxIter",1000);
[U,eneFinale,cvg,outp] = nonlin_min(fct,U,para)
Full example:
clear
pkg load optim
function [x,y] = geometryGenerator(r,elts = 100)
teta = linspace(0,pi,elts = 100);
x = r * cos(teta);
y = r * sin(teta);
endfunction
function ene = complexFunctionOfEnergyIWrap (x,y,E,P, X,Y)
ene = 0;
for i = 1:length(x)-1
ene += E*(x(i)/X(i))^4+ E*(y(i)/Y(i))^4- P *(x(i)^2+(x(i+1)^2)-x(i)*x(i+1))*abs(y(i)-y(i+1));
endfor
endfunction
[x,y] = geometryGenerator(5,100)
%Little distance from axis to avoid division by zero
x +=1e-6;
y +=1e-6;
%Saving initial geometry
X = x;
Y = y;
%Vectorisation of the function
%% Initial vector
U = zeros(length(x)*2,1);
U(1:2:end-1) = linspace(min(x),max(x),length(x));
U(2:2:end) = linspace(min(y),max(y),length(y));
%%Constraints
Aeq = zeros(3,length(U));
%%% Blocked bottom
Aeq(1,1) = 1;
Aeq(2,2) = 1;
%%% Sliding top
Aeq(3,end-1) = 1;
%%%Initial condition
beq = zeros(3,1);
beq(1) = U(1);
beq(2) = U(2);
beq(3) = U(end-1);
contEq = #(U) Aeq * U - beq;
%Parameter
Mat = 0.2e9;
pressure = 50;
%% Vectorized function. Non geometric argument are not optimised, and fixed during calculation
fct =#(U)complexFunctionOfEnergyIWrap(U(1:2:end-1),U(2:2:end), Mat, pressure, X, Y)
para = optimset("Algorithm","lm_feasible","f_equc_idx",contEq,"MaxIter",1000);
[U,eneFinale,cvg,outp] = nonlin_min(fct,U,para)
xFinal = U(1:2:end-1);
yFinal = U(2:2:end);
plot(x,y,';Initial geo;',xFinal,yFinal,'--x;Final geo;')
Finite Element Method is typically formulated as the optimal criteria for the minimization problem, which is equivalent to the Virtual Work Principle (see books like Hughes of Bathe). The Virtual Work, represents a set of linear (or nonlinear) equations which can be solved more efficiently (with fsolve).
If for some motive you must solve the problem as an optimization problem, then, if you are considering linear elasticity, your strain energy is quadratic, thus you could use the qp Octave function.
To use sparse matrices could also be helpful.

How to increase RGB darkness?

I am trying to increase darkness of RGB using pure math without relying on any framework ..
So, here is my implementation:
Provided with R, G, B ( each from 0 to 256 ) & darknessFactor ( 0 to 1 )
// this is a pseudocode
r*= darknessFactor;
g*= darknessFactor;
b*= darknessFactor;
r= int (r);
g= int (g);
b= int (b);
g <<= 8;
r <<= 16;
final_color= b + g + r ;
Then, when I tried to use it against R= 00, G= 256, B= 0, darknessFactor= 0.1, I get a result near to dark red!
Any idea?
EDIT
Would ColorTransform of actionScript 3 solve this problem?
In principle, your pseudo-code looks good, so there must be a problem in your actual code. Although it is really strange that you would have a red result with a green input. With your code, that would be possible for a factor bigger than one, because you're not clamping the channels, so the green channel could overflow into the red one.
This will work:
function clamp(channel:Float) {
var v = Std.int(channel);
return
if (v < 0) 0;
else if (v > 0xFF) 0xFF;
else v;
}
function darken(color:Int, factor:Float) {
var r = (color >> 16) & 0xFF;
var g = (color >> 8) & 0xFF;
var b = color & 0xFF;
return
(clamp(r * factor) << 16)
+ (clamp(g * factor) << 8)
+ clamp(b * factor);
}
You can test it here: http://try.haxe.org/#b328e
you should use HSV intead of RGB.
first, RGB convert to HSV.
second, decrease v(brightness[0 to 1]).
finally, HSV convert to RGB.
you can easily implement converting program.
or, can find library.
ColorTransform would be the simplest way to solve this problem without writing any new code.
See here.
The multiplier properties would be what you want in order to darken the current colour.
var colorInfo:ColorTransform = myDisplayObject.transform.colorTransform;
// Make some color transformations here.
// Reduce colour by 10%
colorInfo.redMultiplier = 0.9;
colorInfo.greenMultiplier = 0.9;
colorInfo.blueMultiplier = 0.9;
// Commit the change.
myDisplayObject.transform.colorTransform = colorInfo;
For an RGB(R1,G1,B1), the new RGB(R2,G2,B2) darkened by the percentage/factor of p% should be:
R2 = R1 - (R1/100)*P
G2 = G1 - (G1/100)*P
B2 = B1 - (B1/100)*P
This is only the solution to your specific problem. If you want to work on something more advanced then please have a look at HSL color space and how to work with its L factor.

how do you reflect a vector over another vector?

I'm using AS3 to program some collision detection for a flash game and am having trouble figuring out how to bounce a ball off of a line. I keep track of a vector that represents the ball's 2D velocity and I'm trying to reflect it over the vector that is perpendicular to the line that the ball's colliding with (aka the normal). My problem is that I don't know how to figure out the new vector (that's reflected over the normal). I figured that you can use Math.atan2 to find the difference between the normal and the ball's vector but I'm not sure how to expand that to solve my problem.
Vector algebra - You want the "bounce" vector:
vec1 is the ball's motion vector and vec2 is the surface/line vector:
// 1. Find the dot product of vec1 and vec2
// Note: dx and dy are vx and vy divided over the length of the vector (magnitude)
var dpA:Number = vec1.vx * vec2.dx + vec1.vy * vec2.dy;
// 2. Project vec1 over vec2
var prA_vx:Number = dpA * vec2.dx;
var prA_vy:Number = dpA * vec2.dy;
// 3. Find the dot product of vec1 and vec2's normal
// (left or right normal depending on line's direction, let's say left)
var dpB:Number = vec1.vx * vec2.leftNormal.dx + vec1.vy * vec2.leftNormal.dy;
// 4. Project vec1 over vec2's left normal
var prB_vx:Number = dpB * vec2.leftNormal.dx;
var prB_vy:Number = dpB * vec2.leftNormal.dy;
// 5. Add the first projection prA to the reverse of the second -prB
var new_vx:Number = prA_vx - prB_vx;
var new_vy:Number = prA_vy - prB_vy;
Assign those velocities to your ball's motion vector and let it bounce.
PS:
vec.leftNormal --> vx = vec.vy; vy = -vec.vx;
vec.rightNormal --> vx = -vec.vy; vy = vec.vx;
The mirror reflection of any vector v from a line/(hyper-)surface with normal n in any dimension can be computed using projection tensors. The parallel projection of v on n is: v|| = (v . n) n = v . nn. Here nn is the outer (or tensor) product of the normal with itself. In Cartesian coordinates it is a matrix with elements: nn[i,j] = n[i]*n[j]. The perpendicular projection is just the difference between the original vector and its parallel projection: v - v||. When the vector is reflected, its parallel projection is reversed while the perpendicular projection is retained. So the reflected vector is:
v' = -v|| + (v - v||) = v - 2 v|| = v . (I - 2 nn) = v . R( n ), where
R( n ) = I - 2 nn
(I is the identity tensor which in Cartesian coordinates is simply the diagonal identity matrix diag(1))
R is called the reflection tensor. In Cartesian coordinates it is a real symmetric matrix with components R[i,j] = delta[i,j] - 2*n[i]*n[j], where delta[i,j] = 1 if i == j and 0 otherwise. It is also symmetric with respect to n:
R( -n ) = I - 2(-n)(-n) = I - 2 nn = R( n )
Hence it doesn't matter if one uses the outward facing or the inward facing normal n - the result would be the same.
In two dimensions and Cartesian coordinates, R (the matrix representation of R) becomes:
[ R00 R01 ] [ 1.0-2.0*n.x*n.x -2.0*n.x*n.y ]
R = [ ] = [ ]
[ R10 R11 ] [ -2.0*n.x*n.y 1.0-2.0*n.y*n.y ]
The components of the reflected vector are then computed as a row-vector-matrix product:
v1.x = v.x*R00 + v.y*R10
v1.y = v.x*R01 + v.y*R11
or after expansion:
k = 2.0*(v.x*n.x + v.y*n.y)
v1.x = v.x - k*n.x
v1.y = v.y - k*n.y
In three dimensions:
k = 2.0*(v.x*n.x + v.y*n.y + v.z*n.z)
v1.x = v.x - k*n.x
v1.y = v.y - k*n.y
v1.z = v.z - k*n.z
Finding the exact point where the ball will hit the line/wall is more involved - see here.
Calculate two components of the vector.
One component will be the projection of your vector onto the reflecting surface the other component will be the projection on to the surface's normal (which you say you already have). Use dot products to get the projections. Add these two components together by summing the two vectors. You'll have your answer.
You can even calculate the second component A2 as being the original vector minus the first component, so: A2 = A - A1. And then the vector you want is A1 plus the reflected A2 (which is simply -A2 since its perpendicular to your surface) or:
Ar = A1-A2
or
Ar = 2A1 - A which is the same as Ar = -(2A2 - A)
If [Ax,Bx] is your balls velocity and [Wx,Wy] is a unit vector representing the wall:
A1x = (Ax*Wx+Ay*Wy)*Wx;
A1y = (Ax*Wx+Ay*Wy)*Wy;
Arx = 2*A1x - Ax;
Ary = 2*A1y - Ay;

Correct solution for this tensor

I'm implementing the system in this paper and I've come a little unstuck correctly implementing the radial tensor field.
All tensors in this system are of the form given on page 3, section 4
R [ cos(2t), sin(2t); sin(2t), -cos(2t) ]
The radial tensor field is defined as:
R [ yy - xx, -2xy; -2xy, -(yy-xx) ]
In my system I'm only storing R and Theta, since I can calculate the tensor based off just that information. This means I need to calculate R and Theta for the radial tensor. Unfortunately, my attempts at this have failed. Although it looks correct, my solution fails in the top left and bottom right quadrants.
Addendum: Following on from discussion in the comments about the image of the system not working, I'll put some hard numbers here too.
The entire tensor field is 800x480, the center point is at { 400, 240 }, and we're using the standard graphics coordinate system with a negative y axis (ie. origin in the top left).
At { 400, 240 }, the tensor is R = 0, T = 0
At { 200, 120 }, the tensor is R = 2.95936E+9, T = 2.111216
At { 600, 120 }, the tensor is R = 2.95936E+9, T = 1.03037679
I can easily sample any more points which you think may help.
The code I'm using to calculate values is:
float x = i - center.X;
float xSqr = x * x;
float y = j - center.Y;
float ySqr = y * y;
float r = (float)Math.Pow(xSqr + ySqr, 2);
float theta = (float)Math.Atan2((-2 * x * y), (ySqr - xSqr)) / 2;
if (theta < 0)
theta += MathHelper.Pi;
Evidently you are comparing formulas (1) and (2) of the paper. Note the scalar multiple l = || (u_x,u_y) || in formula (1), and identify that with R early in the section. This factor is implicit in formula (2), so to make them match we have to factor R out.
Formula (2) works with an offset from the "center" (x0,y0) of the radial map:
x = xp - x0
y = yp - y0
to form the given 2x2 matrix:
y^2 - x^2 -2xy
-2xy -(y^2 - x^2)
We need to factor out a scalar R from this matrix to get a traceless orthogonal 2x2 matrix as in formula (1):
cos(2t) sin(2t)
sin(2t) -cos(2t)
Since cos^2(2t) + sin^2(2t) = 1 the factor R can be identified as:
R = (y^2 - x^2)^2 + (-2xy)^2 = (x^2 + y^2)^2
leaving a traceless orthogonal 2x2 matrix:
C S
S -C
from which the angle 'tan(2t) = S/C` can be extracted by an inverse trig function.
Well, almost. As belisarius warns, we need to check that angle t is in the correct quadrant. The authors of the paper write at the beginning of Sec. 4 that their "t" (which refers to the tensor) depends on R >= 0 and theta (your t) lying in [0,2pi) according to the formula R [ cos(2t), sin(2t); sin(2t) -cos(2t) ].
Since sine and cosine have period 2pi, t (theta) is only uniquely determined up to an interval of length pi. I suspect the authors meant to write either that 2t lies in [0,2pi) or more simply that t lies in [0,pi). belisarius suggestion to use "the atan2 equivalent" will avoid any division by zero. We may (if the function returns a negative value) need to add pi so that t >= 0. This amounts to adding 2pi to 2t, so it doesn't affect the signs of the entries in the traceless orthogonal matrix (since 'R >= 0` the pattern of signs should agree in formulas (1) and (2) ).