i have a little problem.
I have interval. 250 means 0 and 500 means 2. How i can get number between 0-2 by changing coordinates from 250 to 250.
void MouseButton(int button, int state, int x, int y)
{
// MIN(250) - 0
// MAX(500) - 2
if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
{
pos2[0] = ( (float)x * 2 ) / ((float)m_viewport[2] -1);
printf("%f - %d\n", pos2[0], x);
}
}
Have you tried this:
pos2[0] = ( (float)(x * 2.0) ) / ( (float)(m_viewport[2] * 1.0 - 1) );
float scalar = 2.0f
float lBound = 250.0f;
float rBound = 500.0f;
float t = ((float)x-lBound)/(rBound-lBound)
pos2[0] = std::max(std::min(0.0f, t), 1.0f)*scalar
Related
Question: I need to upgrade an old Embarcadero VCL graphic math application by introducing antialiased lines. So, I wrote in C++ the algorithm indicated in the page: https://en.wikipedia.org/wiki/Xiaolin_Wu%27s_line_algorithm.
How to write correctly the function 'plot' to draw the pixel at (x,y) with a brightness 'c', especially on the Embarcadero VCL.
Solution:
This solution has been possible by the contribution of #Spektre (use of a union to mix colors according to some brightness). pC is a canvas pointer, funcColor is the line intended color, and are properties of the Observer class:
//Antialiased line:
void Observer::aaLine(int x0, int y0, int x1, int y1)
{
union {
uint32_t dd;//The color value
uint8_t db[4];//To work on channels: {00.RR.GG.BB}
} c, c0;//Line color, and background color
//Color mixer, with calculations on each channel, because there is no
//Alpha channel with VCL:
auto plot = [&](int X, int Y, float brightness){
c.dd = funcColor;//Line color
c0.dd = pC->Pixels[X][Y];//Background color
//Find coefficients to simulate transparency, where there is not:
//Front color is augmented when background is decreased:
for(int i = 0; i < 3; ++i)
c.db[i] = int(c.db[i] * brightness + c0.db[i] * (1 - brightness));
//Output obtained by conversion:
pC->Pixels[X][Y] = static_cast<TColor>(c.dd);
};
//Wu's algorithm:
//Fractional part of x:
auto fpart = [](double x) { return x - floor(x); };
auto rfpart = [&](double x) { return 1 - fpart(x); };
bool steep = abs(y1 - y0) > abs(x1 - x0);//Means slope > 45 deg.
if(steep) {
std::swap(x0, y0);
std::swap(x1, y1);
}
if( x0 > x1 ) {
std::swap(x0, x1);
std::swap(y0, y1);
}
double dx = x1 - x0, dy = y1 - y0, gradient = (dx == 0. ? 1. : dy/dx) ;
//Handle first endpoint
double xend = x0,
yend = y0 + gradient * (xend - x0),
xgap = rfpart(x0 + 0.5),
xpxl1 = xend, // this will be used in the main loop
ypxl1 = floor(yend);
if( steep ) {
plot(ypxl1, xpxl1, rfpart(yend) * xgap);
plot(ypxl1+1, xpxl1, fpart(yend) * xgap);
}
else {
plot(xpxl1, ypxl1 , rfpart(yend) * xgap);
plot(xpxl1, ypxl1+1, fpart(yend) * xgap);
}
auto intery = yend + gradient; // first y-intersection for the main loop
//Handle second endpoint
xend = round(x1);
yend = y1 + gradient * (xend - x1);
xgap = fpart(x1 + 0.5);
auto xpxl2 = xend, //this will be used in the main loop
ypxl2 = floor(yend);
if( steep ){
plot(ypxl2 , xpxl2, rfpart(yend) * xgap);
plot(ypxl2+1, xpxl2, fpart(yend) * xgap);
//Main loop:
for(double x = xpxl1 + 1 ; x <= xpxl2 - 1 ; x += 1) {
plot(int(intery) , x, rfpart(intery));
plot(int(intery+1), x, fpart(intery));
intery += gradient;
}
}
else {
plot(xpxl2, ypxl2, rfpart(yend) * xgap);
plot(xpxl2, ypxl2+1, fpart(yend) * xgap);
//Main loop:
for(double x = xpxl1 + 1 ; x <= xpxl2 - 1 ; x += 1) {
plot(x, int(intery), rfpart(intery));
plot(x, int(intery+1), fpart(intery));
intery += gradient;
}
}
}//Observer::aaLine.
The source code above is updated, and works for me as a solution.
The image below comes from tests: Blue's are NOT antialiased, and Red's ones are the results from the solution above. I am satisfied with what I want to do.
I think your problem lies in this:
auto plot = [&](double X, double Y, double brighness){
pC->Pixels[X][Y] = brightness; };
If I understand it correctly pC is some target TCanvas ... this has 2 major problems:
pC->Pixels[X][Y] = brightness; will handle brightness as color according to selected mode (so copy,xor,... or whatever) and not as brightness.
I would use form of alpha blending where you take originaly render color (or background) and wanted color of rendered line and mix it with brightness as parameter:
TColor c0=pC->Pixels[X][Y],c0=color of your line;
// here mix colors c = (c0*(1.0-brightness)) + (c1*brightness)
// however you need to do this according to selected pixelformat of you graphic object and color channel wise...
pC->Pixels[X][Y]=c;
Beware VCL transparency does not use alpha parameter its just opaque or not ... For more info about the mixing see similar:
Digital Differential Analyzer with Wu's Algorithm in OpenGL
especially pay attention to the:
union
{
DWORD dd;
BYTE db[4];
} c,c0;
as TColor is 32bit int anyway ...
speed of pC->Pixels[X][Y] in VCL (or any GDI based api) is pitiful at best
in case you handle many pixels you should consider to use ScanLine[Y] from Graphics::TBitmap ... and render to bitmap as backbufer. This usually improve speed from ~1000 to ~10000 times. for more info see:
Graphics rendering in C++
So I'm trying to rotate a point about another point in a window, drawing it with DirectX. My issue is that the rotation is in a weird shape:
http://prntscr.com/iynh5f
What I'm doing is just rotating a point around the center of a window and drawing lines between the points.
vec2_t vecCenter1 { gui.iWindowSize[ 0 ] / 2.f, gui.iWindowSize[ 1 ] / 2.f };
for ( float i { 0.f }; i < 360.f; i += 2.f )
{
vec2_t vecLocation { vecCenter1.x, vecCenter1.y - 100.f };
static vec2_t vecOldLocation = vecLocation;
vecLocation.Rotate( i, vecCenter1 );
if ( i > 0.f )
Line( vecOldLocation, vecLocation, 2, true, D3DCOLOR_ARGB( 255, 255, 255, 255 ) );
vecOldLocation = vecLocation;
}
Here is my rotation:
void vec2_t::Rotate( float flDegrees, vec2_t vecSubtractVector )
{
flDegrees = ToRadian( flDegrees );
float flSin = sin( flDegrees );
float flCos = cos( flDegrees );
*this -= vecSubtractVector;
x = x * flCos - y * flSin;
y = x * flSin + y * flCos;
*this += vecSubtractVector;
}
I've tried a few different methods of rotation and none of them seem to work. If anyone could tell my what I'm doing wrong, I'd appreciate it.
Key lines:
x = x * flCos - y * flSin;
y = x * flSin + y * flCos; << problem
The second line is using the modified value of x, whereas it should be using the original. You must cache both coordinates (or at least x) before updating:
void vec2_t::Rotate( float flDegrees, vec2_t vecSubtractVector )
{
float flRadians = ToRadian( flDegrees );
float flSin = sin( flRadians );
float flCos = cos( flRadians );
// cache both values + pre-subtract
float xOld = x - vecSubtractVector.x;
float yOld = y - vecSubtractVector.y;
// perform the rotation and add back
x = xOld * flCos - yOld * flSin + vecSubtractVector.x;
y = xOld * flSin + yOld * flCos + vecSubtractVector.y;
}
To get rid of the if-statement in your for-loop, just compute the first point outside the loop, and start from the delta value instead of zero
Don't use static because it might cause thread safety issues (although not important in your case) - just declare it outside the loop
You seem to be missing a line segment - the condition needs to be <= 360.f (ideally plus an epsilon)
vec2_t vecCenter1 = { gui.iWindowSize[ 0 ] / 2.f, gui.iWindowSize[ 1 ] / 2.f };
const float delta_angle = 2.f;
vec2_t vecOldLocation = { vecCenter1.x, vecCenter1.y - 100.f };
for ( float i = delta_angle; i <= 360.f; i += delta_angle ) // complete cycle
{
vec2_t vecLocation = { vecCenter1.x, vecCenter1.y - 100.f };
vecLocation.Rotate( i, vecCenter1 );
Line( vecOldLocation, vecLocation, 2, true, // no if statement
D3DCOLOR_ARGB( 255, 255, 255, 255 ) );
vecOldLocation = vecLocation;
}
I am trying to build a game in Opengl. Before I start making better movement mechanics I want to get collision working. I have cube-cube collision working and I have sphere-sphere collision working, but can't figure out cube-sphere collision. Since I want it in 3d I have the pivot at the center of the objects. Anyone have any suggestions?
EDIT: This is the code I currently have:
bool SphereRectCollision( Sphere& sphere, Rectangle& rect)
{
//Closest point on collision box
float cX, cY;
//Find closest x offset
if( sphere.getCenterX() < rect.GetCenterX())//checks if the center of the circle is to the left of the rectangle
cX = rect.GetCenterX();
else if( sphere.getCenterX() > rect.GetCenterX() + rect.GetWidth()) //checks if the center of the circle is to the right of the rectangle
cX = rect.GetCenterX() + rect.GetWidth();
else //the circle is inside the rectagle
cX = sphere.getCenterX();
//Find closest y offset
if( sphere.getCenterY() > rect.GetCenterY() + rect.GetHeight() )
cY = rect.GetCenterY();
else if( sphere.getCenterY() < rect.GetCenterY() - rect.GetHeight() )
cY = rect.GetCenterY() + rect.GetHeight();
else
cY = sphere.getCenterY();
//If the closest point is inside the circle
if( distanceSquared( sphere.getCenterX(), sphere.getCenterY(), cX, cY ) < sphere.getRadius() * sphere.getRadius() )
{
//This box and the circle have collided
return false;
}
//If the shapes have not collided
return true;
}
float distanceSquared( float x1, float y1, float x2, float y2 )
{
float deltaX = x2 - x1;
float deltaY = y2 - y1;
return deltaX*deltaX + deltaY*deltaY;
}
I found the solution. I had the right idea, but didn't quite know how to execute it:
bool SphereRectCollision( Sphere& sphere, Rectangle& rect)
{
float sphereXDistance = abs(sphere.X - rect.X);
float sphereYDistance = abs(sphere.Y - rect.Y);
float sphereZDistance = abs(sphere.Z - rect.Z);
if (sphereXDistance >= (rect.Width + sphere.Radius)) { return false; }
if (sphereYDistance >= (rect.Height + sphere.Radius)) { return false; }
if (sphereZDistance >= (rect.Depth + sphere.Radius)) { return false; }
if (sphereXDistance < (rect.Width)) { return true; }
if (sphereYDistance < (rect.Height)) { return true; }
if (sphereZDistance < (rect.GetDepth)) { return true; }
float cornerDistance_sq = ((sphereXDistance - rect.Width) * (sphereXDistance - rect.Width)) +
((sphereYDistance - rect.Height) * (sphereYDistance - rect.Height) +
((sphereYDistance - rect.Depth) * (sphereYDistance - rect.Depth)));
return (cornerDistance_sq < (sphere.Radius * sphere.Radius));
}
This algorithm doesn't work when a hit happen on an edge, the 2nd set of if conditions triggers but a collision isn't occuring
I copied this ellipse code directly from the opengl textbook:
void ellipseMidpoint (int xCenter, int yCenter, int Rx, int Ry)
{
int Rx2 = Rx * Rx;
int Ry2 = Ry * Ry;
int twoRx2 = 2 * Rx2;
int twoRy2 = 2 * Ry2;
int p;
int x = 0;
int y = Ry;
int px = 0;
int py = twoRx2 * y;
//initial points in both quadrants
ellipsePlotPoints (xCenter, yCenter, x, y);
//Region 1
p = round (Ry2 - (Rx2 * Ry) + (0.25 * Rx2));
while (px < py) {
x++;
px += twoRy2;
if (p < 0)
p += Ry2 + px;
else {
y--;
py -= twoRx2;
p += Ry2 + px - py;
}
ellipsePlotPoints (xCenter, yCenter, x, y);
}
//Region 2
p = round (Ry2 * (x+0.5) * (x+0.5) + Rx2 * (y-1) * (y-1) - Rx2 * Ry2);
while (y > 0) {
y--;
py -= twoRx2;
if (p > 0)
p += Rx2 - py;
else {
x++;
px += twoRy2;
p += Rx2 - py + px;
}
ellipsePlotPoints (xCenter, yCenter, x, y);
}
}
void ellipsePlotPoints (int xCenter, int yCenter, int x, int y)
{
setPixel (xCenter + x, yCenter + y);
setPixel (xCenter - x, yCenter + y);
setPixel (xCenter + x, yCenter - y);
setPixel (xCenter - x, yCenter - y);
}
void setPixel (GLint xPos, GLint yPos)
{
glBegin (GL_POINTS);
glVertex2i(xPos, yPos);
glEnd();
}
The smaller ellipses seem to be fine but the larger ones are pointy and sort of flat at the ends.
Any ideas why?
Here is a current screenshot:
I think you're encountering overflow. I played with your code. While I never saw exactly the same "lemon" type shapes from your pictures, things definitely fell apart at large sizes, and it was caused by overflowing the range of the int variables used in the code.
For example, look at one of the first assignments:
int py = twoRx2 * y;
If you substitute, this becomes:
int py = 2 * Rx * Rx * Ry;
If you use a value of 1000 each for Rx and Ry, this is 2,000,000,000. Which is very close to the 2^31 - 1 top of the range of a 32-bit int.
If you want to use this algorithm for larger sizes, you could use 64-bit integer variables. Depending on your system, the type would be long or long long. Or more robustly, int64_t after including <stdint.h>.
Now, if all you want to do is draw an ellipsis with OpenGL, there are much better ways. The Bresenham type algorithms used in your code are ideal if you need to draw a curve pixel by pixel. But OpenGL is a higher level API, which knows how to render more complex primitives than just pixels. For a curve, you will most typically use a connected set of line segments to approximate the curve. OpenGL will then take care of turning those line segments into pixels.
The simplest way to draw an ellipsis is to directly apply the parametric representation. With phi an angle between 0 and PI, and using the naming from your code, the points on the ellipsis are:
x = xCenter + Rx * cos(phi)
y = yCenter + Ry * sin(phi)
You can use an increment for phi that meets your precision requirements, and the code will look something to generate an ellipsis approximated by DIV_COUNT points will look something like this:
float angInc = 2.0f * m_PI / (float)DIV_COUNT;
float ang = 0.0f;
glBegin(GL_LINE_LOOP);
for (int iDiv = 0; iDiv < DIV_COUNT; ++iDiv) {
ang += angInc;
float x = xCenter + Rx * cos(ang);
float y = yCenter + Ry * sin(ang);
glVertex2f(x, y);
glEnd();
If you care about efficiency, you can avoid calculating the trigonometric functions for each point, and apply an incremental rotation to calculate each point from the previous one:
float angInc = 2.0f * M_PI / (float)DIV_COUNT;
float cosInc = cos(angInc);
float sinInc = sin(angInc);
float cosAng = 1.0f;
float sinAng = 0.0f
glBegin(GL_LINE_LOOP);
for (int iDiv = 0; iDiv < DIV_COUNT; ++iDiv) {
float newCosAng = cosInc * cosAng - sinInc * sinAng;
sinAng = sinInc * cosAng + cosInc * sinAng;
cosAng = newCosAng;
float x = xCenter + Rx * cosAng;
float y = yCenter + Ry * sinAng;
glVertex2f(x, y);
glEnd();
This code is of course just for illustrating the math, and to get you started. In reality, you should use current OpenGL rendering methods, which includes vertex buffers, etc.
i am working on an implementation of the Separting Axis Theorem for use in 2D games. It kind of works but just kind of.
I use it like this:
bool penetration = sat(c1, c2) && sat(c2, c1);
Where c1 and c2 are of type Convex, defined as:
class Convex
{
public:
float tx, ty;
public:
std::vector<Point> p;
void translate(float x, float y) {
tx = x;
ty = y;
}
};
(Point is a structure of float x, float y)
The points are typed in clockwise.
My current code (ignore Qt debug):
bool sat(Convex c1, Convex c2, QPainter *debug)
{
//Debug
QColor col[] = {QColor(255, 0, 0), QColor(0, 255, 0), QColor(0, 0, 255), QColor(0, 0, 0)};
bool ret = true;
int c1_faces = c1.p.size();
int c2_faces = c2.p.size();
//For every face in c1
for(int i = 0; i < c1_faces; i++)
{
//Grab a face (face x, face y)
float fx = c1.p[i].x - c1.p[(i + 1) % c1_faces].x;
float fy = c1.p[i].y - c1.p[(i + 1) % c1_faces].y;
//Create a perpendicular axis to project on (axis x, axis y)
float ax = -fy, ay = fx;
//Normalize the axis
float len_v = sqrt(ax * ax + ay * ay);
ax /= len_v;
ay /= len_v;
//Debug graphics (ignore)
debug->setPen(col[i]);
//Draw the face
debug->drawLine(QLineF(c1.tx + c1.p[i].x, c1.ty + c1.p[i].y, c1.p[(i + 1) % c1_faces].x + c1.tx, c1.p[(i + 1) % c1_faces].y + c1.ty));
//Draw the axis
debug->save();
debug->translate(c1.p[i].x, c1.p[i].y);
debug->drawLine(QLineF(c1.tx, c1.ty, ax * 100 + c1.tx, ay * 100 + c1.ty));
debug->drawEllipse(QPointF(ax * 100 + c1.tx, ay * 100 + c1.ty), 10, 10);
debug->restore();
//Carve out the min and max values
float c1_min = FLT_MAX, c1_max = FLT_MIN;
float c2_min = FLT_MAX, c2_max = FLT_MIN;
//Project every point in c1 on the axis and store min and max
for(int j = 0; j < c1_faces; j++)
{
float c1_proj = (ax * (c1.p[j].x + c1.tx) + ay * (c1.p[j].y + c1.ty)) / (ax * ax + ay * ay);
c1_min = min(c1_proj, c1_min);
c1_max = max(c1_proj, c1_max);
}
//Project every point in c2 on the axis and store min and max
for(int j = 0; j < c2_faces; j++)
{
float c2_proj = (ax * (c2.p[j].x + c2.tx) + ay * (c2.p[j].y + c2.ty)) / (ax * ax + ay * ay);
c2_min = min(c2_proj, c2_min);
c2_max = max(c2_proj, c2_max);
}
//Return if the projections do not overlap
if(!(c1_max >= c2_min && c1_min <= c2_max))
ret = false; //return false;
}
return ret; //return true;
}
What am i doing wrong? It registers collision perfectly but is over sensitive on one edge (in my test using a triangle and a diamond):
//Triangle
push_back(Point(0, -150));
push_back(Point(0, 50));
push_back(Point(-100, 100));
//Diamond
push_back(Point(0, -100));
push_back(Point(100, 0));
push_back(Point(0, 100));
push_back(Point(-100, 0));
I am getting this mega-adhd over this, please help me out :)
http://u8999827.fsdata.se/sat.png
OK, I was wrong the first time. Looking at your picture of a failure case it is obvious a separating axis exists and is one of the normals (the normal to the long edge of the triangle). The projection is correct, however, your bounds are not.
I think the error is here:
float c1_min = FLT_MAX, c1_max = FLT_MIN;
float c2_min = FLT_MAX, c2_max = FLT_MIN;
FLT_MIN is the smallest normal positive number representable by a float, not the most negative number. In fact you need:
float c1_min = FLT_MAX, c1_max = -FLT_MAX;
float c2_min = FLT_MAX, c2_max = -FLT_MAX;
or even better for C++
float c1_min = std::numeric_limits<float>::max(), c1_max = -c1_min;
float c2_min = std::numeric_limits<float>::max(), c2_max = -c2_min;
because you're probably seeing negative projections onto the axis.