In cocos2d the gradient is implemented using a quad, and the color is interpolated by opengl, but there is an extra parameter which controls the gradient's direction, so how does the algorithm to work.
//_alongVector is the gradient's direction
float h = _alongVector.getLength();
if (h == 0)
return;
// why a sqrt(2.0) ???
float c = sqrtf(2.0f);
Vec2 u(_alongVector.x / h, _alongVector.y / h);
// and what does this mean
if (_compressedInterpolation)
{
float h2 = 1 / ( fabsf(u.x) + fabsf(u.y) );
u = u * (h2 * (float)c);
}
float opacityf = (float)_displayedOpacity / 255.0f;
Color4F S(
_displayedColor.r / 255.0f,
_displayedColor.g / 255.0f,
_displayedColor.b / 255.0f,
_startOpacity * opacityf / 255.0f
);
Color4F E(
_endColor.r / 255.0f,
_endColor.g / 255.0f,
_endColor.b / 255.0f,
_endOpacity * opacityf / 255.0f
);
// what are these magic ??????
// (-1, -1)
_squareColors[0].r = E.r + (S.r - E.r) * ((c + u.x + u.y) / (2.0f * c));
_squareColors[0].g = E.g + (S.g - E.g) * ((c + u.x + u.y) / (2.0f * c));
_squareColors[0].b = E.b + (S.b - E.b) * ((c + u.x + u.y) / (2.0f * c));
_squareColors[0].a = E.a + (S.a - E.a) * ((c + u.x + u.y) / (2.0f * c));
// (1, -1)
_squareColors[1].r = E.r + (S.r - E.r) * ((c - u.x + u.y) / (2.0f * c));
_squareColors[1].g = E.g + (S.g - E.g) * ((c - u.x + u.y) / (2.0f * c));
_squareColors[1].b = E.b + (S.b - E.b) * ((c - u.x + u.y) / (2.0f * c));
_squareColors[1].a = E.a + (S.a - E.a) * ((c - u.x + u.y) / (2.0f * c));
// (-1, 1)
_squareColors[2].r = E.r + (S.r - E.r) * ((c + u.x - u.y) / (2.0f * c));
_squareColors[2].g = E.g + (S.g - E.g) * ((c + u.x - u.y) / (2.0f * c));
_squareColors[2].b = E.b + (S.b - E.b) * ((c + u.x - u.y) / (2.0f * c));
_squareColors[2].a = E.a + (S.a - E.a) * ((c + u.x - u.y) / (2.0f * c));
// (1, 1)
_squareColors[3].r = E.r + (S.r - E.r) * ((c - u.x - u.y) / (2.0f * c));
_squareColors[3].g = E.g + (S.g - E.g) * ((c - u.x - u.y) / (2.0f * c));
_squareColors[3].b = E.b + (S.b - E.b) * ((c - u.x - u.y) / (2.0f * c));
_squareColors[3].a = E.a + (S.a - E.a) * ((c - u.x - u.y) / (2.0f * c));
and here is another implementation in cocos2d-objc
// _vector apparently points towards the first color.
float g0 = 0.0f; // (0, 0) dot _vector
float g1 = -_vector.x; // (0, 1) dot _vector
float g2 = -_vector.x - _vector.y; // (1, 1) dot _vector
float g3 = -_vector.y; // (1, 0) dot _vector
float gmin = MIN(MIN(g0, g1), MIN(g2, g3));
float gmax = MAX(MAX(g0, g1), MAX(g2, g3));
GLKVector4 a = GLKVector4Make(_color.r*_color.a*_displayColor.a, _color.g*_color.a*_displayColor.a, _color.b*_color.a*_displayColor.a, _color.a*_displayColor.a);
GLKVector4 b = GLKVector4Make(_endColor.r*_endColor.a*_displayColor.a, _endColor.g*_endColor.a*_displayColor.a, _endColor.b*_endColor.a*_displayColor.a, _endColor.a*_displayColor.a);
_colors[0] = GLKVector4Lerp(a, b, (g0 - gmin)/(gmax - gmin));
_colors[1] = GLKVector4Lerp(a, b, (g1 - gmin)/(gmax - gmin));
_colors[2] = GLKVector4Lerp(a, b, (g2 - gmin)/(gmax - gmin));
_colors[3] = GLKVector4Lerp(a, b, (g3 - gmin)/(gmax - gmin));
are these algorithms the same?
I really want to know the math or algorithm behind those magic operations
Related
I use these camera extrinsics parameters to transform .ply file through PCL, but the result is not correct. I think it is because of the formula is not correct from Quaternion to matrix4f.
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
master
Eigen::Matrix4f transform_1 = Eigen::Matrix4f::Identity();
qw = 0.980613;
qx = -0.0777902;
qy = -0.176786;
qz = -0.0330758,
tx = -0.798112;
ty = -0.774293;
tz = 3.76053;
transform_1 (0, 0) = 1 - 2 * pow(qy, 2) - 2 * pow(qz, 2);
transform_1 (0, 1) = 2 * qx*qy - 2 * qz*qw;
transform_1 (0, 2) = 2 * qx*qz + 2 * qy*qw;
transform_1 (0, 3) = tx;
transform_1 (1, 0) = 2 * qx*qy + 2 * qz*qw;
transform_1 (1, 1) = 1 - 2 * pow(qx,2) - 2 * pow(qz,2);
transform_1 (1, 2) = 2 * qy*qz - 2 * qx*qw;
transform_1 (1, 3) = ty;
transform_1 (2, 0) = 2 * qx*qz - 2 * qy*qw;
transform_1 (2, 1) = 2 * qy*qz + 2 * qx*qw;
transform_1 (2, 2) = 1 - 2 * pow(qx,2) - 2 * pow(qy,2);
transform_1 (2, 3) = tz;
transform_1(3, 0) = 0;
transform_1(3, 1) = 0;
transform_1(3, 2) = 0;
transform_1(3, 3) = 1;
sub01
Eigen::Matrix4f transform_2 = Eigen::Matrix4f::Identity();
qw = 0.861117;
qx = -0.0716478;
qy = 0.427619;
qz = 0.265493,
tx = -2.94326;
ty = -1.91445;
tz = 6.074;
transform_2(0, 0) = 1 - 2 * pow(qy, 2) - 2 * pow(qz, 2);
transform_2(0, 1) = 2 * qx*qy - 2 * qz*qw;
transform_2(0, 2) = 2 * qx*qz + 2 * qy*qw;
transform_2(0, 3) = tx;
transform_2(1, 0) = 2 * qx*qy + 2 * qz*qw;
transform_2(1, 1) = 1 - 2 * pow(qx, 2) - 2 * pow(qz, 2);
transform_2(1, 2) = 2 * qy*qz - 2 * qx*qw;
transform_2(1, 3) = ty;
transform_2(2, 0) = 2 * qx*qz - 2 * qy*qw;
transform_2(2, 1) = 2 * qy*qz + 2 * qx*qw;
transform_2(2, 2) = 1 - 2 * pow(qx, 2) - 2 * pow(qy, 2);
transform_2(2, 3) = tz;
transform_2(3, 0) = 0;
transform_2(3, 1) = 0;
transform_2(3, 2) = 0;
transform_2(3, 3) = 1;
I use cloudcompare also got the same result
var angle1 = 45;
var angle2 = 0;
var L1 = 200;var L2 = 200;
var m1 = 1;
var m2 = 1;
var angleV1 = 0;
var angleV2 = 0;
var g = 1;
var angleA1 =
((-g * (2 * m1 + m2) * sin(angle1) -
m2 * g * sin(angle1 - angle2) -
2 *
sin(angle1 - angle2) *
m2 *
(angleV2 * angleV2 * L2 +
angleV1 * angleV1 * L1 * cos(angle1 - angle2))) /
L1) *
(2 * m1 + m2 - m2 * cos(2 * angle1 - 2 * angle2));
var angleA2 =
((2 *
sin(angle1 - angle2) *
(angleV1 * angleV1 * L1 * (m1 + m2) +
g * (m1 + m2) * cos(angle1) +
angle2 * angle2 * L2 * m2 * cos(angle1 - angle2))) /
L2) *
(2 * m1 + m2 - m2 * cos(2 * angle1 - 2 * angle2));
angleV1 += angleA1;
angle1 += angleV1;
angleV2 += angleA2;
angle2 += angleV2;
var x1 = sin(angle1) * L1;
var y1 = cos(angle1) * L1;
var x2 = x1 + sin(angle2) * L2;
var y2 = y1 + cos(angle2) * L2;
line(0, 0, x1, y1);
line(x1, y1, x2, y2);
I have implemented a code for image warping using bilinear interpolation:
Matlab image rotation
I would like to improve the code by using bicubic interpolation to rotate the image WITHOUT using the built-in functions like imrotate or imwarp and interp functions in MATLAB.
I successfully managed to implement a full working example.
Code is based on Anna1994's code: Matlab image rotation
Biqubic code is also based on Java (and C++) implementation posted here: http://www.paulinternet.nl/?page=bicubic
The following code applies image rotation example using biqubic interpolation:
function BicubicInterpolationTest()
close all;
% clear all;
img = 'cameraman.tif';
input_image =double(imread(img))./255;
H=size(input_image,1); % height
W=size(input_image,2); % width
th=120*pi/180; %Rotate 120 degrees
s0 = 2;
s1 = 2;
x0 = -W/2;
x1 = -H/2;
T=[1 0 x0 ; ...
0 1 x1 ; ...
0 0 1];
RST = [ (s0*cos(th)) (-s1*sin(th)) ((s0*x0*cos(th))-(s1*x1*sin(th))); ...
(s0*sin(th)) (s1*cos(th)) ((s0*x0*sin(th))+(s1*x1*cos(th))); ...
0 0 1];
M=inv(T)*RST;
N = inv(M);
output_image=zeros(H,W,size(input_image,3));
for i=1:W
for j=1:H
x = [i ; j ; 1];
y = N * x;
a = y(1)/y(3);
b = y(2)/y(3);
%Nearest neighbor
% a = round(a);
% b = round(b);
%Bilinear interpolation (applies RGB image):
% x1 = floor(a);
% y1 = floor(b);
% x2 = x1 + 1;
% y2 = y1 + 1;
% if ((x1 >= 1) && (y1 >= 1) && (x2 <= W) && (y2 <= H))
% %Load 2x2 pixels
% i11 = input_image(y1, x1, :); %Top left pixel
% i21 = input_image(y2, x1, :); %Bottom left pixel
% i12 = input_image(y1, x2, :); %Top right pixel
% i22 = input_image(y2, x2, :); %Bottom right pixel
%
% %Interpolation wieghts
% dx = x2 - a;
% dy = y2 - b;
%
% %Bi-lienar interpolation
% output_image(j, i, :) = i11*dx*dy + i21*dx*(1-dy) + i12*(1-dx)*dy + i22*(1-dx)*(1-dy);
% end
x1 = floor(a);
y1 = floor(b);
%Bicubic interpolation (applies grayscale image)
if ((x1 >= 2) && (y1 >= 2) && (x1 <= W-2) && (y1 <= H-2))
%Load 4x4 pixels
P = input_image(y1-1:y1+2, x1-1:x1+2);
%Interpolation wieghts
dx = a - x1;
dy = b - y1;
%Bi-bicubic interpolation
output_image(j, i) = bicubicInterpolate(P, dx, dy);
end
end
end
imshow(output_image);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%Verify implementation by comparing with Matalb build in function imwarp:
tform = affine2d(M');
ref_image = imwarp(input_image, tform, 'OutputView', imref2d(size(input_image)), 'Interp', 'cubic');
figure;imshow(ref_image)
figure;imshow(output_image - ref_image)
max_diff = max(abs(output_image(:) - ref_image(:)));
disp(['Maximum difference from imwarp = ', num2str(max_diff)]);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%http://www.paulinternet.nl/?page=bicubic
%double cubicInterpolate (double p[4], double x) {
% return p[1] + 0.5 * x*(p[2] - p[0] + x*(2.0*p[0] - 5.0*p[1] + 4.0*p[2] - p[3] + x*(3.0*(p[1] - p[2]) + p[3] - p[0])));
%}
function q = cubicInterpolate(p, x)
q = p(2) + 0.5 * x*(p(3) - p(1) + x*(2.0*p(1) - 5.0*p(2) + 4.0*p(3) - p(4) + x*(3.0*(p(2) - p(3)) + p(4) - p(1))));
%http://www.paulinternet.nl/?page=bicubic
% double bicubicInterpolate (double p[4][4], double x, double y) {
% double arr[4];
% arr[0] = cubicInterpolate(p[0], y);
% arr[1] = cubicInterpolate(p[1], y);
% arr[2] = cubicInterpolate(p[2], y);
% arr[3] = cubicInterpolate(p[3], y);
% return cubicInterpolate(arr, x);
% }
function q = bicubicInterpolate(p, x, y)
q1 = cubicInterpolate(p(1,:), x);
q2 = cubicInterpolate(p(2,:), x);
q3 = cubicInterpolate(p(3,:), x);
q4 = cubicInterpolate(p(4,:), x);
q = cubicInterpolate([q1, q2, q3, q4], y);
I verified implementation by comparing to Matalb build in function imwarp
Result:
The following example uses the "CachedBicubicInterpolator" code version, and also supports RGB image:
function BicubicInterpolationTest2()
close all;
% clear all;
img = 'peppers.png';
input_image = double(imread(img))./255;
H=size(input_image,1); % height
W=size(input_image,2); % width
th=120*pi/180; %Rotate 120 degrees
s0 = 0.8;
s1 = 0.8;
x0 = -W/2;
x1 = -H/2;
T=[1 0 x0 ; ...
0 1 x1 ; ...
0 0 1];
RST = [ (s0*cos(th)) (-s1*sin(th)) ((s0*x0*cos(th))-(s1*x1*sin(th))); ...
(s0*sin(th)) (s1*cos(th)) ((s0*x0*sin(th))+(s1*x1*cos(th))); ...
0 0 1];
M=inv(T)*RST;
N = inv(M);
output_image=zeros(H,W,size(input_image,3));
for i=1:W
for j=1:H
x = [i ; j ; 1];
y = N * x;
a = y(1)/y(3);
b = y(2)/y(3);
x1 = floor(a);
y1 = floor(b);
%Bicubic interpolation (applies grayscale image)
if ((x1 >= 2) && (y1 >= 2) && (x1 <= W-2) && (y1 <= H-2))
%Load 4x4 pixels
P = input_image(y1-1:y1+2, x1-1:x1+2, :);
%Interpolation wieghts
dx = a - x1;
dy = b - y1;
%Bi-bicubic interpolation
output_image(j, i, :) = bicubicInterpolate(P, dx, dy);
end
end
end
imshow(output_image);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%Verify implementation by comparing with Matalb build in function imwarp:
tform = affine2d(M');
ref_image = imwarp(input_image, tform, 'OutputView', imref2d(size(input_image)), 'Interp', 'cubic');
figure;imshow(ref_image)
figure;imshow(abs(output_image - ref_image), []);impixelinfo
max_diff = max(abs(output_image(:) - ref_image(:)));
disp(['Maximum difference from imwarp = ', num2str(max_diff)]);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [p0, p1, p2, p3] = list4(P)
P = squeeze(P);
p0 = P(1, :);
p1 = P(2, :);
p2 = P(3, :);
p3 = P(4, :);
%http://www.paulinternet.nl/?page=bicubic
% public void updateCoefficients (double[][] p) {
% a00 = p[1][1];
% a01 = -.5*p[1][0] + .5*p[1][2];
% a02 = p[1][0] - 2.5*p[1][1] + 2*p[1][2] - .5*p[1][3];
% a03 = -.5*p[1][0] + 1.5*p[1][1] - 1.5*p[1][2] + .5*p[1][3];
% a10 = -.5*p[0][1] + .5*p[2][1];
% a11 = .25*p[0][0] - .25*p[0][2] - .25*p[2][0] + .25*p[2][2];
% a12 = -.5*p[0][0] + 1.25*p[0][1] - p[0][2] + .25*p[0][3] + .5*p[2][0] - 1.25*p[2][1] + p[2][2] - .25*p[2][3];
% a13 = .25*p[0][0] - .75*p[0][1] + .75*p[0][2] - .25*p[0][3] - .25*p[2][0] + .75*p[2][1] - .75*p[2][2] + .25*p[2][3];
% a20 = p[0][1] - 2.5*p[1][1] + 2*p[2][1] - .5*p[3][1];
% a21 = -.5*p[0][0] + .5*p[0][2] + 1.25*p[1][0] - 1.25*p[1][2] - p[2][0] + p[2][2] + .25*p[3][0] - .25*p[3][2];
% a22 = p[0][0] - 2.5*p[0][1] + 2*p[0][2] - .5*p[0][3] - 2.5*p[1][0] + 6.25*p[1][1] - 5*p[1][2] + 1.25*p[1][3] + 2*p[2][0] - 5*p[2][1] + 4*p[2][2] - p[2][3] - .5*p[3][0] + 1.25*p[3][1] - p[3][2] + .25*p[3][3];
% a23 = -.5*p[0][0] + 1.5*p[0][1] - 1.5*p[0][2] + .5*p[0][3] + 1.25*p[1][0] - 3.75*p[1][1] + 3.75*p[1][2] - 1.25*p[1][3] - p[2][0] + 3*p[2][1] - 3*p[2][2] + p[2][3] + .25*p[3][0] - .75*p[3][1] + .75*p[3][2] - .25*p[3][3];
% a30 = -.5*p[0][1] + 1.5*p[1][1] - 1.5*p[2][1] + .5*p[3][1];
% a31 = .25*p[0][0] - .25*p[0][2] - .75*p[1][0] + .75*p[1][2] + .75*p[2][0] - .75*p[2][2] - .25*p[3][0] + .25*p[3][2];
% a32 = -.5*p[0][0] + 1.25*p[0][1] - p[0][2] + .25*p[0][3] + 1.5*p[1][0] - 3.75*p[1][1] + 3*p[1][2] - .75*p[1][3] - 1.5*p[2][0] + 3.75*p[2][1] - 3*p[2][2] + .75*p[2][3] + .5*p[3][0] - 1.25*p[3][1] + p[3][2] - .25*p[3][3];
% a33 = .25*p[0][0] - .75*p[0][1] + .75*p[0][2] - .25*p[0][3] - .75*p[1][0] + 2.25*p[1][1] - 2.25*p[1][2] + .75*p[1][3] + .75*p[2][0] - 2.25*p[2][1] + 2.25*p[2][2] - .75*p[2][3] - .25*p[3][0] + .75*p[3][1] - .75*p[3][2] + .25*p[3][3];
% }
% public double getValue (double x, double y) {
% double x2 = x * x;
% double x3 = x2 * x;
% double y2 = y * y;
% double y3 = y2 * y;
%
% return (a00 + a01 * y + a02 * y2 + a03 * y3) +
% (a10 + a11 * y + a12 * y2 + a13 * y3) * x +
% (a20 + a21 * y + a22 * y2 + a23 * y3) * x2 +
% (a30 + a31 * y + a32 * y2 + a33 * y3) * x3;
% }
function q = bicubicInterpolate(P, x, y)
[p00, p01, p02, p03] = list4(P(1, :, :));
[p10, p11, p12, p13] = list4(P(2, :, :));
[p20, p21, p22, p23] = list4(P(3, :, :));
[p30, p31, p32, p33] = list4(P(4, :, :));
a00 = p11;
a01 = -.5*p10 + .5*p12;
a02 = p10 - 2.5*p11 + 2*p12 - .5*p13;
a03 = -.5*p10 + 1.5*p11 - 1.5*p12 + .5*p13;
a10 = -.5*p01 + .5*p21;
a11 = .25*p00 - .25*p02 - .25*p20 + .25*p22;
a12 = -.5*p00 + 1.25*p01 - p02 + .25*p03 + .5*p20 - 1.25*p21 + p22 - .25*p23;
a13 = .25*p00 - .75*p01 + .75*p02 - .25*p03 - .25*p20 + .75*p21 - .75*p22 + .25*p23;
a20 = p01 - 2.5*p11 + 2*p21 - .5*p31;
a21 = -.5*p00 + .5*p02 + 1.25*p10 - 1.25*p12 - p20 + p22 + .25*p30 - .25*p32;
a22 = p00 - 2.5*p01 + 2*p02 - .5*p03 - 2.5*p10 + 6.25*p11 - 5*p12 + 1.25*p13 + 2*p20 - 5*p21 + 4*p22 - p23 - .5*p30 + 1.25*p31 - p32 + .25*p33;
a23 = -.5*p00 + 1.5*p01 - 1.5*p02 + .5*p03 + 1.25*p10 - 3.75*p11 + 3.75*p12 - 1.25*p13 - p20 + 3*p21 - 3*p22 + p23 + .25*p30 - .75*p31 + .75*p32 - .25*p33;
a30 = -.5*p01 + 1.5*p11 - 1.5*p21 + .5*p31;
a31 = .25*p00 - .25*p02 - .75*p10 + .75*p12 + .75*p20 - .75*p22 - .25*p30 + .25*p32;
a32 = -.5*p00 + 1.25*p01 - p02 + .25*p03 + 1.5*p10 - 3.75*p11 + 3*p12 - .75*p13 - 1.5*p20 + 3.75*p21 - 3*p22 + .75*p23 + .5*p30 - 1.25*p31 + p32 - .25*p33;
a33 = .25*p00 - .75*p01 + .75*p02 - .25*p03 - .75*p10 + 2.25*p11 - 2.25*p12 + .75*p13 + .75*p20 - 2.25*p21 + 2.25*p22 - .75*p23 - .25*p30 + .75*p31 - .75*p32 + .25*p33;
x2 = x * x;
x3 = x2 * x;
y2 = y * y;
y3 = y2 * y;
% q = (a00 + a01 * y + a02 * y2 + a03 * y3) +...
% (a10 + a11 * y + a12 * y2 + a13 * y3) * x +...
% (a20 + a21 * y + a22 * y2 + a23 * y3) * x2 +...
% (a30 + a31 * y + a32 * y2 + a33 * y3) * x3;
q = (a00 + a01 * x + a02 * x2 + a03 * x3) +...
(a10 + a11 * x + a12 * x2 + a13 * x3) * y +...
(a20 + a21 * x + a22 * x2 + a23 * x3) * y2 +...
(a30 + a31 * x + a32 * x2 + a33 * x3) * y3;
Result:
I use the following code to extend line segment to the boundry of rectangle, it work well if points within the rectangle, but if there's a point out of the rectangle boundry it fail
static void extend(Rectangle bounds, ref PointF start, ref PointF end)
{
if (start != end) // this to avoid small changes in orientation
{
float slope = (end.Y - start.Y) / (end.X - start.X);
if (Math.Round(start.Y, 2) == Math.Round(end.Y, 2) || Math.Abs(slope) <= 0.01d) // 0.01 is offset to check if the slope is very small
{
start.X = bounds.X;
start.Y = start.Y;
end.X = bounds.X + bounds.Width;
end.Y = end.Y;
return;
}
if (Math.Round(start.X, 2) == Math.Round(end.X, 2) || Math.Abs(slope) <= 0.01d)
{
start.X = start.X;
start.Y = bounds.Y;
end.X = end.X;
end.Y = bounds.Y + bounds.Height;
return;
}
// based on (y - y1) / (x - x1) == (y2 - y1) / (x2 - x1)
// => (y - y1) * (x2 - x1) == (y2 - y1) * (x - x1)
// y_for_xmin = y1 + (y2 - y1) * (xmin - x1) / (x2 - x1)
float y_for_xmin = start.Y + ((end.Y - start.Y) * (bounds.X - start.X) / (end.X - start.X));
// y_for_xmax = y1 + (y2 - y1) * (xmax - x1) / (x2 - x1)
float y_for_xmax = start.Y + ((end.Y - start.Y) * (bounds.X + bounds.Width - start.X) / (end.X - start.X));
// x_for_ymin = x1 + (x2 - x1) * (ymin - y1) / (y2 - y1)
float x_for_ymin = start.X + ((end.X - start.X) * (bounds.Y - start.Y) / (end.Y - start.Y));
//x_for_ymax = x1 + (x2 - x1) * (ymax - y1) / (y2 - y1)
float x_for_ymax = start.X + ((end.X - start.X) * (bounds.Y + bounds.Height - start.Y) / (end.Y - start.Y));
if ((bounds.Y <= y_for_xmin) && (y_for_xmin <= bounds.Y + bounds.Height))
{
if ((bounds.X <= x_for_ymax) && (bounds.X <= bounds.X + bounds.Width))
{
start.X = bounds.X;
start.Y = y_for_xmin;
end.X = x_for_ymax;
end.Y = bounds.Y + bounds.Height;
return;
}
if ((bounds.X <= x_for_ymin && x_for_ymin <= bounds.X + bounds.Width))
{
start.X = bounds.X;
start.Y = y_for_xmin;
end.X = x_for_ymin;
end.Y = bounds.Y;
return;
}
}
if ((bounds.Y <= y_for_xmax) && (bounds.Y <= bounds.Y + bounds.Height))
{
if ((bounds.X <= x_for_ymin) && (x_for_ymin <= bounds.X + bounds.Width))
{
start.X = x_for_ymin;
start.Y = bounds.Y;
end.X = bounds.X + bounds.Width;
end.Y = y_for_xmax;
return;
}
if ((bounds.X <= x_for_ymax) && (x_for_ymax <= bounds.X + bounds.Width))
{
start.X = x_for_ymax;
start.Y = bounds.Y + bounds.Height;
end.X = bounds.X + bounds.Width;
end.Y = y_for_xmax;
return;
}
}
}
}
any idea how to solve the case of points of line out of the rectangle
static bool intersection(PointF a1, PointF a2, PointF b1, PointF b2, ref PointF ans)
{
float x = ((a1.X*a2.Y - a1.Y*a2.X)*(b1.X - b2.X) - (a1.X - a2.X)*(b1.X*b2.Y - b1.Y*b2.X)) / ((a1.X - a2.X)*(b1.Y - b2.Y) - (a1.Y - a2.Y)*(b1.X - b2.X));
float y = ((a1.X*a2.Y - a1.Y*a2.X)*(b1.Y - b2.Y) - (a1.Y - a2.Y)*(b1.X*b2.Y - b1.Y*b2.X)) / ((a1.X - a2.X)*(b1.Y - b2.Y) - (a1.Y - a2.Y)*(b1.X - b2.X));
if(x == float.NaN || x == float.PositiveInfinity || x == float.NegativeInfinity || y == float.NaN || y == float.PositiveInfinity || y == float.NegativeInfinity)
{ // the lines are equal or never intersect
return false;
}
ans.X = x;
ans.Y = y;
return true;
}
static void extend(Rectangle bounds, ref PointF start, ref PointF end)
{
List<PointF> ansFinal = new List<PointF>();
PointF ansLeft = new PointF();
bool hitLeft = intersection(start, end, new PointF(bounds.X, bounds.Y), new PointF(bounds.X, bounds.Y + bounds.Height), ansLeft);
if(hitLeft && (ansLeft.Y < bounds.Y || ansLeft.Y > bounds.Y + bounds.Height)) hitLeft = false;
if(hitLeft) ansFinal.Add(ansLeft);
PointF ansTop = new PointF();
bool hitTop = intersection(start, end, new PointF(bounds.X, bounds.Y), new PointF(bounds.X + bounds.Width, bounds.Y), ansTop);
if(hitTop && (ansTop.X < bounds.X || ansTop.X > bounds.X + bounds.Width)) hitTop = false;
if(hitTop) ansFinal.Add(ansTop);
PointF ansRight = new PointF();
bool hitRight = intersection(start, end, new PointF(bounds.X + bounds.Width, bounds.Y), new PointF(bounds.X + bounds.Width, bounds.Y + bounds.Height), ansRight);
if(hitRight && (ansRight.Y < bounds.Y || ansRight.Y > bounds.Y + bounds.Height)) hitRight = false;
if(hitRight) ansFinal.Add(ansRight);
PointF ansBottom = new PointF();
bool hitBottom = intersection(start, end, new PointF(bounds.X, bounds.Y + bounds.Height), new PointF(bounds.X + bounds.Height, bounds.Y + bounds.Height), ansBottom);
if(hitBottom && (ansBottom.X < bounds.X || ansBottom.X > bounds.X + bounds.Width)) hitBottom = false;
if(hitBottom) ansFinal.Add(ansBottom);
if(!hitLeft && !hitTop && !hitRight && !hitBottom)
{
throw new Exception("No interections");
}
/*
// IF YOU HAD LINQ
PointF[] ans = ansFinal.Distinct().ToArray();
if(ans.Length < 2)
{
throw new Exception("Corner case *wink*");
}
start.X = ans[0].X; start.Y = ans[0].Y;
end.X = ans[1].X; end.Y = ans[1].Y;
*/
// the following is sufficient to cull out corner to corner, one corner
for(int x=ansFinal.Count-1; x>=1; x--)
if(ansFinal[x] == ansFinal[x-1])
ansFinal.RemoveAt(x);
if(ansFinal.Count < 2)
{
throw new Exception("Corner case *wink*");
}
start.X = ansFinal[0].X; start.Y = ansFinal[0].Y;
end.X = ansFinal[1].X; end.Y = ansFinal[1].Y;
}
EDIT I wrote this inside the browser so there may be a few syntax errors...
The concept is you test using Line to Line intersection with each side of the rectangle. If an intersection exists you make sure it's within the bounds of the rectangles side (line segment).
Assuming I have the following Projection Matrix (its computed using gluPerspective(40, 1.0, 0.2, 200.0); on the identity matrix):
2.75, 0.00, 0.00, 0.00,
0.00, 2.75, 0.00, 0.00,
0.00, 0.00, -1.00, -1.00,
-0.00, -0.00, -0.40, -0.00
and want to do an glTranslatef(0.0, 0.0, -10.0); command on this matrix. With openGL ES 1.1 function I recieve (that seems correct in my opinion):
2.75, 0.00, 0.00, 0.00,
0.00, 2.75, 0.00, 0.00,
0.00, 0.00, -1.00, -1.00,
0.00, 0.00, 9.62, 10.00
But if I use my own implementation (openGL ES 2.0), I got some strange behavior:
2.75, 0.00, 0.00, 0.00,
0.00, 2.75, 0.00, 0.00,
0.00, 0.00, 9.00, -1.00,
0.00, 0.00, -0.40, 0.00
My code is quite similiar to Apples openGLES1/2 Example and this site http://iphonedevelopment.blogspot.com/2009/06/opengl-es-from-ground-up-part-7_04.html:
void Matrix::translate(GLfloat xTranslate, GLfloat yTranslate, GLfloat zTranslate){
GLfloat matrix[16];
matrix[0] = matrix[5] = matrix[10] = matrix[15] = 1.0;
matrix[1] = matrix[2] = matrix[3] = matrix[4] = 0.0;
matrix[6] = matrix[7] = matrix[8] = matrix[9] = 0.0;
matrix[11] = 0.0;
matrix[12] = xTranslate;
matrix[13] = yTranslate;
matrix[14] = zTranslate;
multiMatrix(matrix);
}
void Matrix::multiMatrix(const GLfloat* a){
GLfloat* matrix;
GLfloat b[16];
matrix = currentMatrix(); //gets the ProjectionMatrix as GLfloat*
copyMatrix(matrix, b);
matrix[0] = a[0] * b[0] + a[4] * b[1] + a[8] * b[2] + a[12] * b[3];
matrix[1] = a[1] * b[0] + a[5] * b[1] + a[9] * b[2] + a[13] * b[3];
matrix[2] = a[2] * b[0] + a[6] * b[1] + a[10] * b[2] + a[14] * b[3];
matrix[3] = a[3] * b[0] + a[7] * b[1] + a[11] * b[2] + a[15] * b[3];
matrix[4] = a[0] * b[4] + a[4] * b[5] + a[8] * b[6] + a[12] * b[7];
matrix[5] = a[1] * b[4] + a[5] * b[5] + a[9] * b[6] + a[13] * b[7];
matrix[6] = a[2] * b[4] + a[6] * b[5] + a[10] * b[6] + a[14] * b[7];
matrix[7] = a[3] * b[4] + a[7] * b[5] + a[11] * b[6] + a[15] * b[7];
matrix[8] = a[0] * b[8] + a[4] * b[9] + a[8] * b[10] + a[12] * b[11];
matrix[9] = a[1] * b[8] + a[5] * b[9] + a[9] * b[10] + a[13] * b[11];
matrix[10] = a[2] * b[8] + a[6] * b[9] + a[10] * b[10] + a[14] * b[11];
matrix[11] = a[3] * b[8] + a[7] * b[9] + a[11] * b[10] + a[15] * b[11];
matrix[12] = a[0] * b[12] + a[4] * b[13] + a[8] * b[14] + a[12] * b[15];
matrix[13] = a[1] * b[12] + a[5] * b[13] + a[9] * b[14] + a[13] * b[15];
matrix[14] = a[2] * b[12] + a[6] * b[13] + a[10] * b[14] + a[14] * b[15];
matrix[15] = a[3] * b[12] + a[7] * b[13] + a[11] * b[14] + a[15] * b[15];
}
void Matrix::copyMatrix(const GLfloat* source, GLfloat* destination){
for(int i=0; i<16; i++){
destination[i] = source[i];
}
}
Im debugging this for 2 days now and have now idea what Im missing... Any ideas?
I always refer to this excellent implementation. The code is easy to follow and it just works.
Their translate method is pasted here:
translate: function (tx, ty, tz) {
this.elements[3*4+0] += this.elements[0*4+0] * tx + this.elements[1*4+0] * ty + this.elements[2*4+0] * tz;
this.elements[3*4+1] += this.elements[0*4+1] * tx + this.elements[1*4+1] * ty + this.elements[2*4+1] * tz;
this.elements[3*4+2] += this.elements[0*4+2] * tx + this.elements[1*4+2] * ty + this.elements[2*4+2] * tz;
this.elements[3*4+3] += this.elements[0*4+3] * tx + this.elements[1*4+3] * ty + this.elements[2*4+3] * tz;
return this;
},
It's a bit hard to tell from your code, but are you intending to use row-order or column-order for your matrices? Row-order would be more common, but you are creating a column-order translation matrix. A translation matrix has the translation 3-vector running down the right-most column from the top-right corner, not along the bottom row.
In a row-ordered matrix, the rows are contiguous blocks so the first four numbers are the top row. In a column-ordered matrix, the columns are contiguous blocks so the first four numbers are the first column.
If I'm reading your code correctly, then it looks like your multiMatrix function is right-multiplying the parameter matrix against the standing class matrix, using row-order. That would be correct for translate if the matrix was row-ordered, so you need to transpose the translation matrix you are creating.