Fractal Tree - branches not drawn - c++

Currently, I'm trying to draw a symmetric binary tree through IFS (Iterated Function Systems):
but the result is always only the branch tips:
.
I can't figure out what I'm doing wrong or what I'm missing.
This is the IFS:
This is my code:
RenderWindow window(VideoMode(480, 640), "fractals everywhere");
CircleShape point(1);
int chance;
float x, y, w, h, nx, ny, px, py;
void SymmetricBinaryTrees()
{
float r = 0.57f;
float o = 0.785f;
chance = rand() % 3;
switch (chance)
{
case 0:
nx = r * cos(o) * x + (-1 * r * sin(o) * y);
ny = r * sin(o) * x + r * cos(o) * y + 1;
break;
case 1:
nx = r * cos(o) * x + r * sin(o) * y;
ny = -1 * r * sin(o) * x + r * cos(o) * y + 1;
break;
case 2:
nx = x;
ny = y;
break;
}
}
void nextPoint()
{
SymmetricBinaryTrees();
x = nx; y = ny;
}
void drawPoint()
{
px = _map(x, -1.078, 1.078f, 0, w); py = _map(y, 0.f, 2.078f, h, 0); // maps the position accordingly
point.setPosition(px, py);
window.draw(point);
}
int main()
{
srand(time(NULL));
w = window.getSize().x * 1.f;
h = window.getSize().y * 1.f;
x = 0.f; y = 0.f;
window.setFramerateLimit(60);
while (window.isOpen())
{
Event e;
while (window.pollEvent(e))
if (e.type == Event::Closed) window.close();
for (int i = 1; i <= 500; i++)
{
drawPoint();
nextPoint();
}
window.display();
}
return 0;
}
This is the website that I'm using for my code.
If anyone could help me or has any idea I'd be very grateful, thank you.

I share #beyond opinion, I think you're complicating things too much. It will be easier with a different approach. Let's make things easier.
With a recursive function, we can easily understand what should be done each step.
Consider we start from a initial point, then trace a line on an angle of a given lenght, so we need a function like:
void createTreeRecursive(sf::VertexArray &tree, sf::Vector2f point, float angle, float lenght)
Where tree will be our line set, which compose the tree itself.
First thing we can do, is to set the first point, which is already known:
// Add first point
tree.append(sf::Vertex(point, treeColor));
Now we need to calculate our next point, to form a line. With simple trigonometric functions we can determine that point:
float newX = point.x + (cos((2.f * PI / 360.f) * angle) * lenght);
float newY = point.y - (sin((2.f * PI / 360.f) * angle) * lenght); // Caution here! Minus(-) sign because we're drawing upwards
So we add this second point, and then split the tree into 2 new branches, each one rotated some certain degrees:
// Add second point
tree.append(sf::Vertex(nextPoint, treeColor));
// Create sub-tree from 2nd point, rotating +45 degrees (i.e. counterclockwise), reducing lenght of the new branch by 0.6 factor
createTreeRecursive(tree, nextPoint, angle + O, lenght * R);
// Same with the other sub-tree, but rotating -45 (i.e. clockwise)
createTreeRecursive(tree, nextPoint, angle - O, lenght * R);
We need a base case for our recursive function, in this case, I choose 3 as minimum lenght:
if (lenght < 3)
// End condition, can be modified
return;
this must be out first check.
So we're done, we only need the initial call:
sf::VertexArray createTree(){
// Our tree will be made out of lines
sf::VertexArray ret(sf::PrimitiveType::Lines);
// Initial point at botton-center(250, 450), with a 90 degrees rotation, first branch lenght 200
createTreeRecursive(ret, sf::Vector2f(250, 450), 90, 200);
return ret;
}
And the result is:
Full code
#include <SFML/Graphics.hpp>
const double PI = 3.141592;
const double R = 0.57; // Reduction factor
const double O = 45; // Degree rotation each time
sf::Color treeColor = sf::Color::Blue;
void createTreeRecursive(sf::VertexArray &tree, sf::Vector2f point, float angle, float lenght){
if (lenght < 3)
// End condition, can be modified
return;
// Add first point
tree.append(sf::Vertex(point, treeColor));
float newX = point.x + (cos((2.f * PI / 360.f) * angle) * lenght);
float newY = point.y - (sin((2.f * PI / 360.f) * angle) * lenght); // Caution here! Minus(-) sign because we're drawing upwards
sf::Vector2f nextPoint(newX, newY);
// Add second point
tree.append(sf::Vertex(nextPoint, treeColor));
// Create sub-tree from 2nd point, rotating +45 degrees (i.e. counterclockwise), reducing lenght of the new branch by 0.6 factor
createTreeRecursive(tree, nextPoint, angle + O, lenght * R);
// Same with the other sub-tree, but rotating -45 (i.e. clockwise)
createTreeRecursive(tree, nextPoint, angle - O, lenght * R);
}
sf::VertexArray createTree(){
// Our tree will be made out of lines
sf::VertexArray ret(sf::PrimitiveType::Lines);
// Initial point at bottom-center(250, 450), with a 90 degrees rotation, first branch lenght 200
createTreeRecursive(ret, sf::Vector2f(250, 450), 90, 200);
return ret;
}
int main()
{
RenderWindow window({ 500, 500 }, "SFML Tree", Style::Close);
auto tree = createTree();
while (window.isOpen())
{
for (Event event; window.pollEvent(event);){
if (event.type == Event::Closed)
window.close();
}
window.clear();
window.draw(tree);
window.display();
}
return EXIT_SUCCESS;
}

I would advice you to use recursion with a function that 1) draws the current branch (as a line), and then 2) creates two new branches from the current branch. Using global variables doesn't help you either. Looks like you should rethink your approach.

For Linux is:
#include <SFML/Graphics.hpp>
#include <cmath>
const double PI = 3.141592;
const double R = 0.57;
const double O = 45;
sf::Color treeColor = sf::Color::Blue;
void createTreeRecursive(sf::VertexArray &tree, sf::Vector2f point, float angle, float lenght){
if (lenght < 3)
return;
tree.append(sf::Vertex(point, treeColor));
float newX = point.x + (cos((2.f * PI / 360.f) * angle) * lenght);
float newY = point.y - (sin((2.f * PI / 360.f) * angle) * lenght);
sf::Vector2f nextPoint(newX, newY);
tree.append(sf::Vertex(nextPoint, treeColor));
createTreeRecursive(tree, nextPoint, angle + O, lenght * R);
createTreeRecursive(tree, nextPoint, angle - O, lenght * R);
}
sf::VertexArray createTree(){
sf::VertexArray ret(sf::PrimitiveType::Lines);
createTreeRecursive(ret, sf::Vector2f(250, 450), 90, 200);
return ret;
}
int main()
{
sf::RenderWindow window({ 500, 500 }, "SFML Tree", sf::Style::Close);
auto tree = createTree();
while (window.isOpen())
{
for (sf::Event event; window.pollEvent(event);){
if (event.type == sf::Event::Closed)
window.close();
}
window.clear();
window.draw(tree);
window.display();
}
return EXIT_SUCCESS;
}

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.

How can I make angles not act weird?

I'm new to programming and SFML. I'm trying to make something like a canon. It's gonna fire balls that will be flying in an arc. Sounds like a very simple task to accomplish, yet I cannot seem to figure out how angles work in SFML. For example, with ang_const = 0.13 Rad (7.44 Deg), my balls flies in a beautiful arc. However, when I change the value of ang_const to 0.14 Rad (8.021 Deg), the ball flies in the opposite direction! If I change the angle to 0.19 Rad (10.88 Deg), it flies downwards for whatever reason.
So here's my code:
#include <SFML/Graphics.hpp>
#include <math.h>
int WIDTH = 1024, HEIGHT = 704;
class Ball {
private:
float radius = 16.00;
public:
sf::CircleShape shape;
Ball () {
shape.setPosition(0 + radius*2, HEIGHT - radius*2);
shape.setRadius(radius);
shape.setFillColor(sf::Color::Cyan);
shape.setOrigin(radius, radius);
}
void update() {
if (x() - radius > WIDTH) {
this->shape.setPosition(0 - radius, y());
}
if (x() + radius < 0) {
this->shape.setPosition(WIDTH + radius, y());
}
if (y() - radius > HEIGHT) {
this->shape.setPosition(x(), 0 - radius);
}
if (y() + radius < 0) {
this->shape.setPosition(x(), HEIGHT + radius);
}
}
float RadToDeg (float radian) {
double pi = 3.14159;
return radian * (180 / pi);
}
float x() { return shape.getPosition().x; }
float y() { return shape.getPosition().y; }
float getRadius() { return radius; }
};
int main()
{
// Create the main window
sf::RenderWindow window(sf::VideoMode(WIDTH, HEIGHT), "del");
// Some variables
float ang_const = 0.13;
float velX_const = 3.5, velY_const = 3.5;
float grav_const = -0.02;
float ang = ang_const;
float velX = velX_const, velY = velY_const;
float grav = grav_const;
// Text
int size_for_text = 64;
sf::Font f;
f.loadFromFile("Keyboard.ttf");
sf::Text text1;
text1.setFont(f);
text1.setCharacterSize(27);
text1.setFillColor(sf::Color::White);
text1.setPosition(size_for_text, size_for_text);
// Ball
Ball ball;
while (window.isOpen())
{
// Process events
sf::Event event;
while (window.pollEvent(event))
{
// Close window: exit
if (event.type == sf::Event::Closed) {
window.close();
}
// Escape pressed: exit
if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape) {
window.close();
}
// Restart
if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Space) {
ang = ang_const;
velX = velX_const, velY = velY_const;
grav = grav_const;
ball.shape.setPosition(0 + ball.getRadius()*2, HEIGHT - ball.getRadius()*2);
}
}
// Ball movement
ball.update();
velY += grav;
ball.shape.move(velX * cos(ball.RadToDeg(ang)),
velY * -sin(ball.RadToDeg(ang)));
// Clear screen
window.clear(sf::Color(0,0,80,100));
// Draw ball
window.draw(ball.shape);
// Draw text
text1.setString("ang " + std::to_string(ang));
window.draw(text1);
// Update the window
window.display();
}
return EXIT_SUCCESS;
}
The main lines are these:
Variables:
float ang_const = 0.13;
float velX_const = 3.5, velY_const = 3.5;
float grav_const = -0.02;
Ball movement:
velY += grav;
ball.shape.move(velX * cos(ball.RadToDeg(ang)), velY * -
sin(ball.RadToDeg(ang)));
Radians to Degrees function:
float RadToDeg (float radian) {
double pi = 3.14159;
return radian * (180 / pi);
}
Could someone explain what's wrong with my code and how angles work in SFML? I'd be appreciated for your help guys.
All the trigonometric functions defined in <cmath> expect their parameters to be values representing angles in radians (see e.g. std::cos).
So, when you write something like
cos(ball.RadToDeg(ang))
where ang is equal to, say 0.13, RadToDeg will convert it to 7.44, but, even if your intention is to pass an angle in degrees, that value is interpreted by std::cos (and std::sin) as an angle of 7.44 radians (or 66.28°).
That leads to your unexpected results:
cosr(7.44) = 0.505 (instead of cosd(7.44°) = 0.993) and
cosr(8.021) = -0.166 (instead of cosd(8.021°) = 0.992)

C++ SDL Angular motion point to point by a click

Im trying to move a object from A point to B point by a click. Each frame the object will move a litlle to the B point. My code works but its doesnt do exactly what I want.
This is what I mean:
I have two code, this is the first one...
class tank {
public:
void setMoveTank(tank &_tank, int x, int y) {
_tank.tanke.moving = true;
_tank.tanke.x2 = x;
_tank.tanke.y2 = y;
int vx = _tank.tanke.x2 - _tank.tanke.x;
int vy = _tank.tanke.y2 - _tank.tanke.y;
if(vx==0) vx+=1; if(vy==0) vy+=1; //float core dumperra arreglateko
double alfa = atan2(vy,vx) * 180 / M_PI;
_tank.tanke.vx = 2*cos(alfa*( M_PI / 180));
_tank.tanke.vy = 2*sin(alfa*( M_PI / 180));
}
void renderTank(render _render, tank &_tank) {
if(_tank.tanke.moving) {
_tank.tanke.x += _tank.tanke.vx;
_tank.tanke.y += _tank.tanke.vy;
}
SDL_Rect dst = {(int)(_tank.tanke.x),(int)(_tank.tanke.y),_tank.tanke.spr.w,_tank.tanke.spr.h};
SDL_RenderCopy(_render.ren, _tank.tanke.img, &_tank.tanke.spr, &dst);
}
};
...and do something like this
I calculate angle once and I move object every frame, but it never arrive to destination
This is my second code...
class tank {
public:
void setMoveTank(tank &_tank, int x, int y) {
_tank.tanke.moving = true;
_tank.tanke.x2 = x;
_tank.tanke.y2 = y;
}
void renderTank(render _render, tank &_tank) {
if(_tank.tanke.moving) {
int vx = _tank.tanke.x2 - _tank.tanke.x;
int vy = _tank.tanke.y2 - _tank.tanke.y;
if(vx==0) vx+=1; if(vy==0) vy+=1; //float core dumperra arreglateko
double alfa = atan2(vy,vx) * 180 / M_PI;
_tank.tanke.vx = 2*cos(alfa*( M_PI / 180));
_tank.tanke.vy = 2*sin(alfa*( M_PI / 180));
_tank.tanke.x += _tank.tanke.vx;
_tank.tanke.y += _tank.tanke.vy;
}
SDL_Rect dst = {(int)(_tank.tanke.x),(int)(_tank.tanke.y),_tank.tanke.spr.w,_tank.tanke.spr.h};
SDL_RenderCopy(_render.ren, _tank.tanke.img, &_tank.tanke.spr, &dst);
}
};
... and do something like this
this code do what I want but I must recalculate de angle each frame
The problem is that, in the second code, the object doesnt draw a rect line and in the first code, the object never arrive to the B point.
Im sure that the problem is in the ROUND, but how can I fix it? What would the best way to make this?
int main() {
if(event.type == SDL_MOUSEBUTTONDOWN) {
switch(event.button.button) {
case SDL_BUTTON_LEFT: _tank.setMoveTank(_tank, event.button.x, event.button.y); break;
}
}
}
SDL_RenderClear(_render.ren);
_tank.renderTank(_render, _tank);
SDL_RenderPresent(_render.ren);
}
}
hiii
EDIT: this is the fixed code
void renderTank(render _render, tank &_tank) {
if(_tank.tanke.moving) {
double vx = _tank.tanke.x2 - _tank.tanke.x;
double vy = _tank.tanke.y2 - _tank.tanke.y;
if(vx==0) vx+=1; if(vy==0) vy+=1; //float core dumperra arreglateko
double alfa = atan2(vy,vx) * 180 / M_PI;
_tank.tanke.vx = 2*cos(alfa*( M_PI / 180));
_tank.tanke.vy = 2*sin(alfa*( M_PI / 180));
_tank.tanke.x += _tank.tanke.vx;
_tank.tanke.y += _tank.tanke.vy;
}
SDL_Rect dst = {(int)(_tank.tanke.x),(int)(_tank.tanke.y),_tank.tanke.spr.w,_tank.tanke.spr.h};
SDL_RenderCopy(_render.ren, _tank.tanke.img, &_tank.tanke.spr, &dst);
}
The problem is that you are storing the velocity as an integer. This causes a lot of precision to be lost, and this is why it goes at a slightly different angle in the first example. In the second example you try to adjust the velocity every frame, which causes it to flip between 2 directions which are both off because of a lack of precision.
Store both the position and velocity as floating point values, and only cast them to integers when you need to draw the tank on the screen.

Why are my openGL ellipses pointed?

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.

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.