How could I fill in my circle with a solid color using the distance formula? - c++

I am a beginner in c++ and have coded a for loop to show a hollow circle when I run the code, however, I was wondering how I could achieve a filled-in circle using the distance formula (d = sqrt((ax-bx)^2 + (ay-by)^2). Here's what I have so far! Any help would be appreciated!
int MAX = 728;
for (float t = 0; t < 2 * 3.14; t += 0.01)
SetPixel(MAX / 4 + MAX / 6 * sin(t), MAX / 4 + MAX / 6 * cos(t), 255, 255, 0);

#include <windows.h>
#include <iostream>
using namespace std;
int main()
{
HWND consoleWindow = GetConsoleWindow(); // Get a console handle
HDC consoleDC = GetDC(consoleWindow); // Get a handle to device context
int max = 628;
float i = 0;
float t;
float doublePi = 6.29;
for (i = 0.0; i < max; i += 2.0) {
for (t = 0.0; t < doublePi; t += 0.01) {
SetPixel(consoleDC, max / 4 + (max - i) / 6 * sin(t), max / 4 + (max - i) / 6 * cos(t), RGB(255, 255, 0));
}
}
ReleaseDC(consoleWindow, consoleDC);
cin.ignore();
return 0;
}
Working almost well. Draw and fill in! A little slow...

Pffff... do not use sin and cos! instead use the sqrt(1-x^2) approach. You can view the formula rendering a circle in google for example: https://www.google.com/search?q=sqrt(1-x^2)
I edit this answer because it seems that is not clear:
float radius = 50.0f;
for (int x = -radius; x <= radius; ++x) {
int d = round(sqrt(1.0f - (x * x / radius / radius)) * radius);
for (int y = -d; y <= d; ++y) {
SetPixel(x, y, 255, 255, 0);
}
}
Note: each graphic library is different, so I assumed that you used rightfully the "SetPixel" function.
Now, for most people say the sqrt(1-x^2) approach should be enough, but it seem that some downvoters does not think the same XD.

Inefficient as can be, and probably the last way you really want to draw a circle ... but ...
Over the entire square encompassing your circle, calculate each pixel's distance from the center and set if under or equal the radius.
// Draw a circle centered at (Xcenter,Ycenter) with given radius using distance formula
void drawCircle(HDC dc, int XCenter, int YCenter, int radius, COLORREF c) {
double fRad = radius * 1.0; // Just a shortcut to avoid thrashing data types
for (int x = XCenter - radius; x<XCenter + radius; x++) {
for (int y = YCenter - radius; y<YCenter + radius; y++) {
double d = sqrt(((x - XCenter) * (x - XCenter)) + ((y - YCenter) * (y - YCenter)) );
if (d <= fRad) SetPixel(dc, x, y, c);
}
}
}
Caveat: No more caveat, used a C++ environment and tested it this time. :-)
Call thusly:
int main()
{
HWND consoleWindow = GetConsoleWindow();
HDC consoleDC = GetDC(consoleWindow);
drawCircle(consoleDC, 50, 50, 20, RGB(255, 0, 255));
ReleaseDC(consoleWindow, consoleDC);
return 0;
}

Related

weird inaccuracy in line rotation - c++

I have programmed a simple dragon curve fractal. It seems to work for the most part, but there is an odd logical error that shifts the rotation of certain lines by one pixel. This wouldn't normally be an issue, but after a few generations, at the right size, the fractal begins to look wonky.
I am using open cv in c++ to generate it, but I'm pretty sure it's a logical error rather than a display error. I have printed the values to the console multiple times and seen for myself that there is a one-digit difference between values that are intended to be the exact same - meaning a line may have a y of 200 at one end and 201 at another.
Here is the full code:
#include<iostream>
#include<cmath>
#include<opencv2/opencv.hpp>
const int width=500;
const int height=500;
const double PI=std::atan(1)*4.0;
struct point{
double x;
double y;
point(double x_,double y_){
x=x_;
y=y_;
}};
cv::Mat img(width,height,CV_8UC3,cv::Scalar(255,255,255));
double deg_to_rad(double degrees){return degrees*PI/180;}
point rotate(int degree, int centx, int centy, int ll) {
double radians = deg_to_rad(degree);
return point(centx + (ll * std::cos(radians)), centy + (ll * std::sin(radians)));
}
void generate(point & r, std::vector < point > & verticies, int rotation = 90) {
int curRotation = 90;
bool start = true;
point center = r;
point rot(0, 0);
std::vector<point> verticiesc(verticies);
for (point i: verticiesc) {
double dx = center.x - i.x;
double dy = center.y - i.y;
//distance from centre
int ll = std::sqrt(dx * dx + dy * dy);
//angle from centre
curRotation = std::atan2(dy, dx) * 180 / PI;
//add 90 degrees of rotation
rot = rotate(curRotation + rotation, center.x, center.y, ll);
verticies.push_back(rot);
//endpoint, where the next centre will be
if (start) {
r = rot;
start = false;
}
}
}
void gen(int gens, int bwidth = 1) {
int ll = 7;
std::vector < point > verticies = {
point(width / 2, height / 2 - ll),
point(width / 2, height / 2)
};
point rot(width / 2, height / 2);
for (int i = 0; i < gens; i++) {
generate(rot, verticies);
}
//draw lines
for (int i = 0; i < verticies.size(); i += 2) {
cv::line(img, cv::Point(verticies[i].x, verticies[i].y), cv::Point(verticies[i + 1].x, verticies[i + 1].y), cv::Scalar(0, 0, 0), 1, 8);
}
}
int main() {
gen(10);
cv::imshow("", img);
cv::waitKey(0);
return 0;
}
First, you use int to store point coordinates - that's a bad idea - you lose all accuracy of point position. Use double or float.
Second, your method for drawing fractals is not too stable numericly. You'd better store original shape and all rotation/translation/scale that indicate where and how to draw scaled copies of the original shape.
Also, I believe this is a bug:
for(point i: verices)
{
...
vertices.push_back(rot);
...
}
Changing size of vertices while inside such a for-loop might cause a crash or UB.
Turns out it was to do with floating-point precision. I changed
x=x_;
y=y_;
to
x=std::round(x_);
y=std::round(y_);
and it works.

Stop spray and stay in its position

// #include loads up library files, the order can matter
// generally load glut.h last
#include <stdio.h> // this library is for standard input and output
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include "glut.h"// this library is for glut the OpenGL Utility Toolkit
//this defines a constant for the array size
#define SPRAYSIZE 500
// the properties of a spray particle are defined in a struct
struct sprayParticle {
float x = 0; // current position x
float y = 0; // current position y
float startx = 0; // birth position x
float starty = 0; // birth position y
int startTime; // a birthtime in frames when it will be born
int startRange = 100; // the maximum time at which a birth can happen
bool started = false; // tracks whether the particle has benn born or not
float speed = 0.1;
float radius;
float startxd = 0; // starting direction vector x value
float startyd = 0; // startingdirection vestor y value
float xd = 0; // current direction vector x value
float yd = 0; // current direction vector x value
float alpha = 1.0; // transparency
};
int winWidth = 1000, winHeight = 1000;
int counter = 0;
time_t t;
sprayParticle spray[SPRAYSIZE];
float angle = 90; // the angle of the spray: 0 degrees is to the left,
// 90 degrees straight up, 180 to the right etc
float sprayWidth = 30;// the width of the spray in degrees
float sprayCenterX, sprayCenterY;
//variables for spray colour, set once per spray
float fr = 1; float fg = 1; float fb = 1;
// the gravity vector
float gx = 0;
float gy = -0.0005;
// the position of thepartcle ystem emitter, wher the rocket should be drawn
float rocketstartx = winWidth - 100, rocketstarty = winHeight - 100;
void init() {
glClearColor(0.0, 0.0, 0.0, 0.0); // set what colour you want the background to be
glMatrixMode(GL_PROJECTION); // set the matrix mode, we will look at this later
gluOrtho2D(0.0, winWidth, 0.0, winHeight);
}
void circle(double radius, double xc, double yc) {
int i;
double angle = 2 * 3.1415 / 20; // circle is drawn using 20 line.
double circle_xy[40][2];
circle_xy[0][0] = radius + xc;
circle_xy[0][1] = yc;
glBegin(GL_POLYGON);
for (i = 1; i < 20; i++) {
circle_xy[i][0] = radius * cos(i *angle) + xc;
circle_xy[i][1] = radius * sin(i * angle) + yc;
glVertex2f(circle_xy[i - 1][0], circle_xy[i - 1][1]);
glVertex2f(circle_xy[i][0], circle_xy[i][1]);
}
glEnd();
}
void normalise(int i) {
float mag;
mag = sqrt((spray[i].xd*spray[i].xd) + (spray[i].yd*spray[i].yd));
spray[i].xd = spray[i].xd / mag;
spray[i].yd = spray[i].yd / mag;
}
// we calculate the direction vector of the current particle from the global
variable angle and spread
void setDirectionVector(int i) {
float minAngle, maxAngle, range, newangle;
double newAngleInRadians; // variable
int rangeInt;
minAngle = angle - (sprayWidth / 2.0); // calc the minimum angle the particle could move along
maxAngle = angle + (sprayWidth / 2.0); // calc the maximum angle
range = maxAngle - minAngle;
rangeInt = (int)(range*100.0);
newangle = minAngle + ((float)(rand() % rangeInt) / 100.0); // generate a random angle between mi and max angles
newAngleInRadians = (double)(newangle / 360.0)*(2 * 3.1415); // convert it to radians
spray[i].xd = (float)cos(newAngleInRadians);// calc the diection vector x value
spray[i].yd = (float)sin(newAngleInRadians);// calc the diection vector y value
}
void initspray() {
for (int i = 0; i < SPRAYSIZE; i++) {
spray[i].x = winWidth / 2; // set current start x position
spray[i].y = 100;// set current start y position
spray[i].startx = spray[i].x; spray[i].starty = spray[i].y;// set start x and y position
spray[i].speed = 0.1 + (float)(rand() % 150) / 1000.0;// speed is 0.1 to 0.25
spray[i].startTime = rand() % spray[i].startRange;// set birth time
spray[i].radius = (float)(rand() % 15); // random radius
setDirectionVector(i);// set the current direction vector
spray[i].startxd = spray[i].xd; spray[i].startyd = spray[i].yd; // set start direction vector to current
}
// set colour of spray
fr = 0.5 + (float)(rand() % 500) / 1000.0;
fg = 0.5 + (float)(rand() % 500) / 1000.0;
fb = 0.5 + (float)(rand() % 500) / 1000.0;
}
void drawsprayParticle(int i) {
glLineWidth(2);
if (!spray[i].started) {
if (counter == spray[i].startTime) {
spray[i].started = true;
}
}
if (spray[i].started) {
glColor4f(fr, fg, fb, spray[i].alpha); // white particiles
circle(spray[i].radius, spray[i].x, spray[i].y);
spray[i].x = spray[i].x + (spray[i].xd*spray[i].speed);
spray[i].y = spray[i].y + (spray[i].yd*spray[i].speed);
// this produces a direction vector that is a little longer than 1
spray[i].yd = spray[i].yd + gy;
// so the normalise the vector to make length 1
normalise(i);
// reduce transparency
spray[i].alpha -= 0.00015;
}
if (spray[i].x<0 || spray[i].x>winWidth + 500 || spray[i].y<0 || spray[i].y>winHeight) {
spray[i].x = spray[i].startx; spray[i].y = spray[i].starty; //rocketstartx
spray[i].xd = spray[i].startxd; spray[i].yd = spray[i].startyd;
spray[i].alpha = 1.0;
}
}
void drawspray() {
// draw each spray particle
for (int i = 0; i < SPRAYSIZE; i++) {
drawsprayParticle(i);
}
// increment rocket position
rocketstartx += 0.2;
// if the rocket is oof the screen more nad 500 pixels to the right the rest it to 0
if (rocketstartx > winWidth + 500) { rocketstartx = 0; }
counter++;
}
// This is the display function it is called when ever you want to draw something
void display() {
glClear(GL_COLOR_BUFFER_BIT); // clear the screen using the background colour
glColor3f(1.0, 1.0, 1.0); // set colour to white
drawspray();
glFlush(); // force all drawing to finish
}
// This is the idle function it is called whenever the program is idle
void idle() {
display();
}
// As with many programming languages the main() function is the entry point for execution of the program
int main(int argc, char** argv) {
srand((unsigned)time(&t));
// initialise first spray work
initspray();
glutInit(&argc, argv); //perform the GLUT initialization
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA); // more initialisation
glutInitWindowSize(winWidth, winHeight); // set window position
glutInitWindowPosition(0, 0); // set window size
glutCreateWindow("Fire"); // create a display with a given caption for the title bar
glEnable(GL_BLEND); //Enable blending.
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
init(); // call init function defined above
glutIdleFunc(idle); // define what function to call when the program is idle
glutDisplayFunc(display); // define what function to call to draw
glutMainLoop();
// this line exits the program
return 0;
}
The original code above normally creates a fountain that sprays particles all over the screen, but I have changed the size and range of the spray, so I can create a flame. The problem is that I can't stop the spread of the particles and it keeps moving up. I want it to stay in its position.
This is how it works:
This is how I want it to work:
With the formula
spray[i].x = spray[i].x + (spray[i].xd*spray[i].speed);
spray[i].y = spray[i].y + (spray[i].yd*spray[i].speed);
the distance of the points to it origin linear increases. You have to decrease the speed by time to smoothly approach to a limit position.
e.g.
spray[i].speed *= 0.9992f;
Of course you have to rest the speed (spray[i].speed), when the point is "restarted" at its origin. If the speed of the point or the alpha value of the point falls below a threshold, then the point has to be "restarted", too:
void drawsprayParticle(int i) {
glLineWidth(2);
if (!spray[i].started) {
if (counter == spray[i].startTime) {
spray[i].started = true;
}
}
if (spray[i].started) {
glColor4f(fr, fg, fb, spray[i].alpha); // white particles
circle(spray[i].radius, spray[i].x, spray[i].y);
spray[i].x = spray[i].x + (spray[i].xd*spray[i].speed);
spray[i].y = spray[i].y + (spray[i].yd*spray[i].speed);
// this produces a direction vector that is a little longer than 1
spray[i].yd = spray[i].yd + gy;
// so the normalize the vector to make length 1
normalise(i);
// reduce transparency
spray[i].alpha -= 0.0003;
spray[i].speed *= 0.9992f;
}
if ( spray[i].x<0 || spray[i].x>winWidth + 500 ||
spray[i].y<0 || spray[i].y>winHeight ||
spray[i].alpha < 1.0f/256.0f ||
spray[i].speed < 0.04f ) {
spray[i].x = spray[i].startx; spray[i].y = spray[i].starty;
spray[i].xd = spray[i].startxd; spray[i].yd = spray[i].startyd;
spray[i].alpha = 1.0;
spray[i].speed = 0.1 + (float)(rand() % 150) / 1000.0;
}
}
This is an empirical algorithm, so you have to play around with the values, to get the effect of your need.
Additionally I increase the start range: int startRange = 1300;
Preview:

Use Kalman Filter to filt noise without delay opencv c++

My task is to detect an orange ball in video. I detected by thresholding image on HSV colorspace and bounding box. Then I have center and radius of ball, with unit is pixel.
When ball is static, I expect center and radius will be static too, but reality, it has noise. I use Kalman Filter to filter noise and it works well. But it delay in real-time. I try to optimize covariance parameters but not work.
So could anyone help me static center and radius when ball is static and without delay?
Are you sure it is the Kalman Filter witch is causing the delay.
Otherwise you can try this lazy filter witch only is noise rejecting but blazingly fast. My suspicion however it is the HSV conversion.
class noiseFilter
{
private:
cv::Point2f ptLast;
float ptMaxTol;
public:
noiseFilter(float maxTol = 1.5f)
{
ptMaxTol = maxTol * maxTol; // we do the pow(2) here so we don't have to do a square root on every update
ptLast = cv::Point2f(0.0f, 0.0f);
}
cv::Point2f update(cv::Point2f &ptNew) // update filter with new found point
{
float dist = pDistance2(ptLast, ptNew);
if (dist > ptMaxTol) ptLast = ptNew; // update only if distance is more than threshold
return ptLast;
}
cv::Point2f getResult() // get result of filter
{
return ptLast;
}
private:
// calculate distance between 2 point without doing a sqrt
float pDistance2(cv::Point2f &p1, cv::Point2f &p2)
{
float dx = p1.x - p2.x;
float dy = p1.y - p2.y;
return (dx * dx + dy * dy);
}
};
int main()
{
cv::Point2f pt;
noiseFilter filter(2.1f); // initialize filter wit max 2.1 pixels noise rejection.
int x = 100, y = 120;
for (int i = 0; i < 100; i++)
{
// generate some noise with 2 pixels variation
pt.x = ((rand() % 200) - 100) * 0.01f + x;
pt.y = ((rand() % 200) - 100) * 0.01f + y;
cv::Point2f pts = filter.update(pt);
printf("input x=%6.2f y=%6.2f output x=%6.2f y=%6.2f\r\n", pt.x, pt.y, pts.x, pts.y);
// do som random big update on random intervals
if ((rand() % 50) == 1) {
x += 15;
printf("big update on X\r\n");
}
if ((rand() % 50) == 1){
y += 25;
printf("big update on Y\r\n");
}
}
return 0;
}
Below a noise filter with smoothing.
Works on slow and fast moving objects.
class noiseFilterSmooth
{
private:
static const int maxHist = 10;
cv::Point2f ptLast;
float ptMaxTol;
cv::Point2f hist[maxHist];
int histHead,histSize;
public:
noiseFilterSmooth(float maxTol = 1.5f)
{
histHead = histSize = 0;
ptMaxTol = maxTol * maxTol; // we do the pow(2) here so we don't have to do a square root on every update
ptLast = cv::Point2f(0.0f, 0.0f);
}
cv::Point2f& update(cv::Point2f &ptNew) // update filter with new found point
{
float dist = pDistance2(ptLast, ptNew);
if (dist > ptMaxTol) histSize = histHead = 0; // reset smoothing filter if distance is more than threshold
// update smoothing filter with last result
hist[histHead] = ptNew; // update smoothing filter with last
histHead = (histHead + 1) % maxHist;
if (histSize < maxHist) histSize++;
return getResult();
}
cv::Point2f& getResult() // get result of filter
{
float sumx = 0, sumy = 0;
for (int i = 0; i < histSize; i++)
{
sumx += hist[i].x;
sumy += hist[i].y;
}
ptLast.x = sumx / histSize;
ptLast.y = sumy / histSize;
return ptLast;
}
private:
// calculate distance between 2 point without doing a sqrt
float pDistance2(cv::Point2f &p1, cv::Point2f &p2)
{
float dx = p1.x - p2.x;
float dy = p1.y - p2.y;
return (dx * dx + dy * dy);
}
};

Perlin Noise getting wrong values in Y axis (C++)

Issue
I'm trying to implement the Perlin Noise algorithm in 2D with a single octave with a size of 16x16. I'm using this as heightmap data for a terrain, however it only seems to work in one axis. Whenever the sample point moves to a new Y section in the Perlin Noise grid, the gradient is very different from what I expect (for example, it often flips from 0.98 to -0.97, which is a very sudden change).
This image shows the staggered terrain in the z direction (which is the y axis in the 2D Perlin Noise grid)
Code
I've put the code that calculates which sample point to use at the end since it's quite long and I believe it's not where the issue is, but essentially I scale down the terrain to match the Perlin Noise grid (16x16) and then sample through all the points.
Gradient At Point
So the code that calculates out the gradient at a sample point is the following:
// Find the gradient at a certain sample point
float PerlinNoise::gradientAt(Vector2 point)
{
// Decimal part of float
float relativeX = point.x - (int)point.x;
float relativeY = point.y - (int)point.y;
Vector2 relativePoint = Vector2(relativeX, relativeY);
vector<float> weights(4);
// Find the weights of the 4 surrounding points
weights = surroundingWeights(point);
float fadeX = fadeFunction(relativePoint.x);
float fadeY = fadeFunction(relativePoint.y);
float lerpA = MathUtils::lerp(weights[0], weights[1], fadeX);
float lerpB = MathUtils::lerp(weights[2], weights[3], fadeX);
float lerpC = MathUtils::lerp(lerpA, lerpB, fadeY);
return lerpC;
}
Surrounding Weights of Point
I believe the issue is somewhere here, in the function that calculates the weights for the 4 surrounding points of a sample point, but I can't seem to figure out what is wrong since all the values seem sensible in the function when stepping through it.
// Find the surrounding weight of a point
vector<float> PerlinNoise::surroundingWeights(Vector2 point){
// Produces correct values
vector<Vector2> surroundingPoints = surroundingPointsOf(point);
vector<float> weights;
for (unsigned i = 0; i < surroundingPoints.size(); ++i) {
// The corner to the sample point
Vector2 cornerToPoint = surroundingPoints[i].toVector(point);
// Getting the seeded vector from the grid
float x = surroundingPoints[i].x;
float y = surroundingPoints[i].y;
Vector2 seededVector = baseGrid[x][y];
// Dot product between the seededVector and corner to the sample point vector
float dotProduct = cornerToPoint.dot(seededVector);
weights.push_back(dotProduct);
}
return weights;
}
OpenGL Setup and Sample Point
Setting up the heightmap and getting the sample point. Variables 'wrongA' and 'wrongA' is an example of when the gradient flips and changes suddenly.
void HeightMap::GenerateRandomTerrain() {
int perlinGridSize = 16;
PerlinNoise perlin_noise = PerlinNoise(perlinGridSize, perlinGridSize);
numVertices = RAW_WIDTH * RAW_HEIGHT;
numIndices = (RAW_WIDTH - 1) * (RAW_HEIGHT - 1) * 6;
vertices = new Vector3[numVertices];
textureCoords = new Vector2[numVertices];
indices = new GLuint[numIndices];
float perlinScale = RAW_HEIGHT/ (float) (perlinGridSize -1);
float height = 50;
float wrongA = perlin_noise.gradientAt(Vector2(0, 68.0f / perlinScale));
float wrongB = perlin_noise.gradientAt(Vector2(0, 69.0f / perlinScale));
for (int x = 0; x < RAW_WIDTH; ++x) {
for (int z = 0; z < RAW_HEIGHT; ++z) {
int offset = (x* RAW_WIDTH) + z;
float xVal = (float)x / perlinScale;
float yVal = (float)z / perlinScale;
float noise = perlin_noise.gradientAt(Vector2( xVal , yVal));
vertices[offset] = Vector3(x * HEIGHTMAP_X, noise * height, z * HEIGHTMAP_Z);
textureCoords[offset] = Vector2(x * HEIGHTMAP_TEX_X, z * HEIGHTMAP_TEX_Z);
}
}
numIndices = 0;
for (int x = 0; x < RAW_WIDTH - 1; ++x) {
for (int z = 0; z < RAW_HEIGHT - 1; ++z) {
int a = (x * (RAW_WIDTH)) + z;
int b = ((x + 1)* (RAW_WIDTH)) + z;
int c = ((x + 1)* (RAW_WIDTH)) + (z + 1);
int d = (x * (RAW_WIDTH)) + (z + 1);
indices[numIndices++] = c;
indices[numIndices++] = b;
indices[numIndices++] = a;
indices[numIndices++] = a;
indices[numIndices++] = d;
indices[numIndices++] = c;
}
}
BufferData();
}
Turned out the issue was in the interpolation stage:
float lerpA = MathUtils::lerp(weights[0], weights[1], fadeX);
float lerpB = MathUtils::lerp(weights[2], weights[3], fadeX);
float lerpC = MathUtils::lerp(lerpA, lerpB, fadeY);
I had the interpolation in the y axis the wrong way around, so it should have been:
lerp(lerpB, lerpA, fadeY)
Instead of:
lerp(lerpA, lerpB, fadeY)

Separating Axis Theorem is driving me nuts!

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.