Is my math wrong? The user is supposed to be able to input an angle in degrees, and it rotate the matrix respectively. Instead, it shrinks the object and flips it... calling
glmxRotate(&modelview, 0.0f, 0.0f, 1.0f, 90.0f);
(with modelview being an identity matrix) yields:
Regular: http://i.imgur.com/eX7Td.png
Rotated: http://i.imgur.com/YnMEn.png
Here's glmxRotate:
glmxvoid glmxRotate(glmxMatrix* matrix, glmxfloat x, glmxfloat y, glmxfloat z,
glmxfloat angle)
{
if(matrix -> mx_size != 4){GLMX_ERROR = GLMX_NOT_4X4; return;}
//convert to rads
angle *= 180.0f / 3.14159;
const glmxfloat len = sqrtf((x * x) + (y * y) + (z * z)),
c = cosf(angle),
c1 = 1.0f - c,
s = sinf(angle);
//normalize vector
x /= len;
y /= len;
z /= len;
glmxfloat rot_mx[] = {x * x * c1 + c,
x * y * c1 + z * s,
x * z * c1 - y * s,
0.0f,
x * y * c1 - z * s,
y * y * c1 + c,
y * z * c1 + x * s,
0.0f,
x * z * c1 + y * s,
y * z * c1 - x * s,
z * z * c1 + c,
0.0f,
0.0f,
0.0f,
0.0f,
1.0f,};
_glmxMultiMatrixArray(matrix, rot_mx, 4);
}
Also, if a translation matrix is defined with the translation in the last four column, how would one go about translating an identity matrix, because the outcome would always yield 0s?
Your matrix looks correct to me, though are you aware that your angle to rads multiplication is actually a radians to angle multiplication?
//convert to rads
angle *= 180.0f / 3.14159;
Should be Pi/180.f.
as there is a limited amount of space that the float is stored in the sin and cos are not exactly calculated this means that small errors happen every time the object is rotated this means that over time the object will get smaller.
if you want this to not happen use Quaternions for rotations.
https://www.npmjs.com/package/quaternion
Related
I have the following parallel projection (Row major):
Before I apply the projection I use the following matrix to apply a transformation on the original points:
EDIT correct MATRIX:Matrix
Where a is z rotation,b is y rotation and g is x rotation and s scales my wireframe height.
And it is giving me the following output:
When in reality it should be giving me this (image not centered):
n is 0.1
and f is 100
The origin is in the middle of the wireframe and the 42 wireframe height is in the +z direction.
My matrix multiplication:
typedef struct s_point
{
float x;
float y;
float z;
int c; //color
} t_point;
void multiply_matrix_vector(t_point *i, t_point *o, t_pmatrix *m)
{
float w;
o->x = i->x * m->m[0][0] + i->y * m->m[1][0] + i->z * m->m[2][0] + m->m[3][0];
o->y = i->x * m->m[0][1] + i->y * m->m[1][1] + i->z * m->m[2][1] + m->m[3][1];
o->z = i->x * m->m[0][2] + i->y * m->m[1][2] + i->z * m->m[2][2] + m->m[3][2];
w = i->x * m->m[0][3] + i->y * m->m[1][3] + i->z * m->m[2][3] + m->m[3][3];
if (w != 0.0f)
{
o->x /= w;
o->y /= w;
o->z /= w;
}
o->c = i->c;
}
What am I doing wrong?
I remembered that I was having a hard time with matrix multiplication before and that probably I might had screwed a sign or a trigonometric function. I double checked everything and but all was ok. Still the result was flipped on the y axis so what I did was this:
Matrix
On the first matrix at [1][1] I just changed the 1 for -1
Matrices are tricky because the multiplication order matters more than one would think
I want to find the point at distance d on the right side of a line defined by P1(x1,y1) and P2(x2,y2) (the distance is calculated from the middle of the line). I came up with the following code, which works well, but I think I have made unnecessary calculations, and it can be done faster.
#define PI 3.141592653589793238462643383279502884197169399375105820974944592308
double x2, x1, y1, y2, px, py, p1x, p1y, p2x, p2y, d, ax, ay, b, dx, dy;
d = 2.0; // given distance
ax = (x1 + x2) / 2; // middle point
ay = (y1 + y2) / 2; // middle point
b = tan(atan2(y2 - y1, x2 - x1) + PI / 2); // slope of the perpendicular line
dx = (d / sqrt(1 + (b * b)));
dy = b * dx;
p1x = ax + dx;
p1y = ay + dy;
p2x = ax - dx;
p2y = ay - dy;
// cross product
if (((x2 - x1) * (p1y - y1) - (y2 - y1) * (p1x - x1)) > 0)
{
px = p1x;
py = p1y;
}
else
{
px = p2x;
py = p2y;
}
You don't need atan, b value, cross product to check orientation (moreover, b might be zero and cause division error).
Instead calculate normalized (unit length) direction vector and get right normal to it:
d = 2.0; // given distance
ax = (x1 + x2) / 2; // middle point
ay = (y1 + y2) / 2; // middle point
dx = x2 - x1;
dy = y2 - y1;
scale = d / sqrt(dx*dx + dy*dy); //distance/vector length
px = ax + dy * scale; // add normal vector to the right side of p1-p2 direction
py = ay - dx * scale; //note minus sign
For generating a 2D vector perpendicular to another, one result that falls out from a special case of the dot product is that you can swap the two components of the vector and negate one of them.
For example, let's say you have the vector d which points from p1 to p2:
dx = p2x - p1x;
dy = p2y - p1y;
And now you want to generate right which is perpendicular, it is simply:
rightx = dy;
righty = -dx;
Now, let's do a quick visual check for our definition of "on the right", in case we actually want to negate those two values...
o p2 = [2, 3]
/
o p1 = [0, 0]
Above, d is simple: [2, 3]. Intuitively, we would think of (as viewed from above) walking from p1 to p2 and looking to the right, which would mean a vector in the positive X direction and the negative Y direction. So yes, that looks fine.
Note: If your co-ordinate system is screen-based (i.e. positive Y direction is down), then the inverse is true (and you would negate both the terms in the calculation of the right vector). This is due to the handedness of the co-ordinate system being left instead of right.
Now, you can calculate the midpoint mid as either (p1 + p2) / 2 or p1 + d / 2.
midx = (p1x + p2x) / 2;
midy = (p1y + p2y) / 2;
And finally to generate p3 you start from mid and extend down the vector right by an amount height, you need to normalize that vector by dividing by its length and scale by height. Formally, the final point will be mid + right * height / length(right).
This is the only particularly expensive part of the calculation, because it needs a square root.
rdist = height / sqrt(rightx * rightx + righty * righty);
p3x = midx + rightx * rdist;
p3y = midy + righty * rdist;
Congratulations! You now have an isosceles triangle!
I'm trying to draw a diagonal semicircle. So far I've only been able to draw ones that begin and end on a horizontal or vertical axis, like this:
I've tried modifying the code to tilt the circle, but it doesn't work. Can someone please tell me where I've gone wrong, this is infuriating!
float theta, tanTheta, x, y, dx, dy;
int circle_points = 1000, radius = 70;
glBegin(GL_POLYGON);
for(int i = 0; i < circle_points; i++)
{
dx = pts[1].x - pts[0].x;
dy = pts[1].y - pts[0].y;
tanTheta = tan(dy / dx);
// get the inverse
theta = atan(tanTheta);
x = radius * cos(theta);
y = radius * sin(theta);
glVertex2f(x, y);
}
glEnd();
I recommend to calculate the angle to the start point and the angle to the end point by atan2.
Interpolate the angle between the start angle and the end angle and draw a line along the corresponding points on the circe:
float ang_start, ang_end, theta, x, y;
ang_start = atan2( pts[0].y, pts[0].x );
ang_end = atan2( pts[1].y, pts[1].x );
if ( ang_start > ang_end )
ang_start -= 2.0f * M_PI;
glBegin(GL_LINE_STRIP);
for(int i = 0; i <= circle_points; i++)
{
float w = (float)i / (float)circle_points;
float theta = ang_start + w * ( ang_end - ang_start );
x = radius * cos(theta);
y = radius * sin(theta);
glVertex2f(x, y);
}
glEnd();
function plot(x, y, c) is
plot the pixel at (x, y) with brightness c (where 0 ≤ c ≤ 1)
function ipart(x) is
return integer part of x
function round(x) is
return ipart(x + 0.5)
function fpart(x) is
return fractional part of x
function rfpart(x) is
return 1 - fpart(x)
function drawLine(x1,y1,x2,y2) is
dx = x2 - x1
dy = y2 - y1
if abs(dx) < abs(dy) then
swap x1, y1
swap x2, y2
swap dx, dy
end if
if x2 < x1
swap x1, x2
swap y1, y2
end if
gradient = dy / dx
// handle first endpoint
xend = round(x1)
yend = y1 + gradient * (xend - x1)
xgap = rfpart(x1 + 0.5)
xpxl1 = xend // this will be used in the main loop
ypxl1 = ipart(yend)
plot(xpxl1, ypxl1, rfpart(yend) * xgap)
plot(xpxl1, ypxl1 + 1, fpart(yend) * xgap)
intery = yend + gradient // first y-intersection for the main loop
// handle second endpoint
xend = round (x2)
yend = y2 + gradient * (xend - x2)
xgap = fpart(x2 + 0.5)
xpxl2 = xend // this will be used in the main loop
ypxl2 = ipart (yend)
plot (xpxl2, ypxl2, rfpart (yend) * xgap)
plot (xpxl2, ypxl2 + 1, fpart (yend) * xgap)
// main loop
for x from xpxl1 + 1 to xpxl2 - 1 do
plot (x, ipart (intery), rfpart (intery))
plot (x, ipart (intery) + 1, fpart (intery))
intery = intery + gradient
end function
What does this mean?
function fpart(x) is
return fractional part of x
How do I get the fractional part of x?
Fractional part is the part after the decimal point, for eg. the fractional part of 10.5 is 0.5. Assuming x is a floating-point number, x - floor(x) will get you the fractional part.
You should return the fractional part, which is what comes after the decimal.
4.34 = 0.33 ect...
I created this function that draws a simple polygon with n number of vertexes:
void polygon (int n)
{
double pI = 3.141592653589;
double area = min(width / 2, height / 2);
int X = 0, Y = area - 1;
double offset = Y;
int lastx, lasty;
double radius = sqrt(X * X + Y * Y);
double quadrant = atan2(Y, X);
int i;
for (i = 1; i <= n; i++)
{
lastx = X; lasty = Y;
quadrant = quadrant + pI * 2.0 / n;
X = round((double)radius * cos(quadrant));
Y = round((double)radius * sin(quadrant));
setpen((i * 255) / n, 0, 0, 0.0, 1); // r(interval) g b, a, size
moveto(offset + lastx, offset + lasty); // Moves line offset
lineto(offset + X, offset + Y); // Draws a line from offset
}
}
How can I fill it with a solid color?
I have no idea how can I modify my code in order to draw it filled.
The common approach to fill shapes is to find where the edges of the polygon cross either each x or each y coordinate. Usually, y coordinates are used, so that the filling can be done using horizontal lines. (On framebuffer devices like VGA, horizontal lines are faster than vertical lines, because they use consecutive memory/framebuffer addresses.)
In that vein,
void fill_regular_polygon(int center_x, int center_y, int vertices, int radius)
{
const double a = 2.0 * 3.14159265358979323846 / (double)vertices;
int i = 1;
int y, px, py, nx, ny;
if (vertices < 3 || radius < 1)
return;
px = 0;
py = -radius;
nx = (int)(0.5 + radius * sin(a));
ny = (int)(0.5 - radius * cos(a));
y = -radius;
while (y <= ny || ny > py) {
const int x = px + (nx - px) * (y - py) / (ny - py);
if (center_y + y >= 0 && center_y + y < height) {
if (center_x - x >= 0)
moveto(center_x - x, center_y + y);
else
moveto(0, center_y + y);
if (center_x + x < width)
lineto(center_x + x, center_y + y);
else
lineto(width - 1, center_y + y);
}
y++;
while (y > ny) {
if (nx < 0)
return;
i++;
px = nx;
py = ny;
nx = (int)(0.5 + radius * sin(a * (double)i));
ny = (int)(0.5 - radius * cos(a * (double)i));
}
}
}
Note that I only tested the above with a simple SVG generator, and compared the drawn lines to the polygon. Seems to work correctly, but use at your own risk; no guarantees.
For general shapes, use your favourite search engine to look for "polygon filling" algorithms. For example, this, this, this, and this.
There are 2 different ways to implement a solution:
Scan-line
Starting at the coordinate that is at the top (smallest y value), continue to scan down line by line (incrementing y) and see which edges intersect the line.
For convex polygons you find 2 points, (x1,y) and (x2,y). Simply draw a line between those on each scan-line.
For concave polygons this can also be a multiple of 2. Simply draw lines between each pair. After one pair, go to the next 2 coordinates. This will create a filled/unfilled/filled/unfilled pattern on that scan line which resolves to the correct overall solution.
In case you have self-intersecting polygons, you would also find coordinates that are equal to some of the polygon points, and you have to filter them out. After that, you should be in one of the cases above.
If you filtered out the polygon points during scan-lining, don't forget to draw them as well.
Flood-fill
The other option is to use flood-filling. It has to perform more work evaluating the border cases at every step per pixel, so this tends to turn out as a slower version. The idea is to pick a seed point within the polygon, and basically recursively extend up/down/left/right pixel by pixel until you hit a border.
The algorithm has to read and write the entire surface of the polygon, and does not cross self-intersection points. There can be considerable stack-buildup (for naive implementations at least) for large surfaces, and the reduced flexibility you have for the border condition is pixel-based (e.g. flooding into gaps when other things are drawn on top of the polygon). In this sense, this is not a mathematically correct solution, but it works well for many applications.
The most efficient solution is by decomposing the regular polygon in trapezoids (and one or two triangles).
By symmetry, the vertexes are vertically aligned and it is an easy matter to find the limiting abscissas (X + R cos(2πn/N) and X + R cos(2π(+1)N)).
You also have the ordinates (Y + R sin(2πn/N) and Y + R sin(2π(+1)N)) and it suffices to interpolate linearly between two vertexes by Y = Y0 + (Y1 - Y0) (X - X0) / (X1 - X0).
Filling in horizontal runs is a little more complex, as the vertices may not be aligned horizontally and there are more trapezoids.
Anyway, it seems that I / solved / this myself again, when not relying on assistance (or any attempt for it)
void polygon (int n)
{
double pI = 3.141592653589;
double area = min(width / 2, height / 2);
int X = 0, Y = area - 1;
double offset = Y;
int lastx, lasty;
while(Y-->0) {
double radius = sqrt(X * X + Y * Y);
double quadrant = atan2(Y, X);
int i;
for (i = 1; i <= n; i++)
{
lastx = X; lasty = Y;
quadrant = quadrant + pI * 2.0 / n;
X = round((double)radius * cos(quadrant));
Y = round((double)radius * sin(quadrant));
//setpen((i * 255) / n, 0, 0, 0.0, 1);
setpen(255, 0, 0, 0.0, 1); // just red
moveto(offset + lastx, offset + lasty);
lineto(offset + X, offset + Y);
} }
}
As you can see, it isn't very complex, which means it might not be the most efficient solution either.. but it is close enough.
It decrements radius and fills it by virtue of its smaller version with smaller radius.
On that way, precision plays an important role and the higher n is the less accuracy it will be filled with.