Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Separating axis theorem: Circle and rotated square  (Read 2366 times)

0 Members and 1 Guest are viewing this topic.

Carlitox

  • Jr. Member
  • **
  • Posts: 53
    • View Profile
Separating axis theorem: Circle and rotated square
« on: January 19, 2015, 04:02:34 pm »
In one tutorial I've found on the internet tells that I must project the center of the circle into the 3 axis, 2 axis will be from the square and one is the vector from the center of the circle to the nearest point of the square.

My problem it's that I don't know how to add the radius to the center proyection. Now the rectangle collides only with the circle center but I don't know how to collide the perimeter.

http://www.sevenson.com.au/actionscript/sat/



bool satBoxCircle(const sf::RectangleShape& object1, const sf::CircleShape& object2)
{
        sf::Vector2f Points[4];
        Points[0] = object1.getTransform().transformPoint(0.f, 0.f);
        Points[1] = object1.getTransform().transformPoint(object1.getSize().x, 0.f);
        Points[2] = object1.getTransform().transformPoint(object1.getSize().x, object1.getSize().y);
        Points[3] = object1.getTransform().transformPoint(0.f, object1.getSize().y);
        sf::Vector2f circle_center(object2.getPosition().x + object2.getRadius(), object2.getPosition().y + object2.getRadius());

        //Find nearest square vertex to the circle center
        int index = 0;
        float min_distance = distance(Points[0], circle_center);
//Proyect square
        for (int i = 0; i < 4; i++)
        {
                if (distance(Points[i], circle_center) < min_distance)
                {
                        min_distance = distance(Points[i], circle_center);
                        index = i;
                }
        }

        sf::Vector2f Axis[3] = {
                sf::Vector2f(Points[1].x - Points[0].x, Points[1].y - Points[0].y),
                sf::Vector2f(Points[1].x - Points[2].x, Points[1].y - Points[2].y),
                sf::Vector2f(Points[index].x - circle_center.x, Points[index].y - circle_center.y)
        };

        //Proyect each point into each axis and find min and max
        for (int i = 0; i < 3; i++)
        {
                float box_min = 0.f, box_max = 0.f;
                box_min = dotProduct(Points[0], Axis[i]);
                box_max = box_min;

                for (int j = 0; j<4; j++)
                {
                        float projection = dotProduct(Points[j], Axis[i]);
                        if (projection<box_min) box_min = projection;
                        if (projection>box_max) box_max = projection;
                }

                float circle_center_projection = dotProduct(circle_center, Axis[i]);
                if (!((circle_center_projection <= box_max)) && (circle_center_projection >= box_min)) return false;
        }
       
        return true;
}
 
« Last Edit: January 20, 2015, 01:56:10 am by Carlitox »

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 10836
    • View Profile
    • development blog
    • Email
Re: Separating axis theorem: Circle and rodated square
« Reply #1 on: January 19, 2015, 04:57:32 pm »
Since you project the center point of a circle onto an axis and since a circle's maximum width is always 2*radius, the "min/max" are simple that projected point + the radius and projected point - the radius.



(I'm so bad at drawing stuff ;D)
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Carlitox

  • Jr. Member
  • **
  • Posts: 53
    • View Profile
Re: Separating axis theorem: Circle and rodated square
« Reply #2 on: January 19, 2015, 08:07:00 pm »
Ok, I find the  two points of the perimeter using trigonometry and I proyect it in each axis, but I fail. The collision is produced but in 2 quadrant you can enter inside the circle a little and in the other 1 the collsion is is a little bit outside. In the north east quadrant the collision is produced too many pixels outside.

PD: Sorry, works fine thank you.  :)

float findAngle(sf::Vector2f v)
{
        return atan(v.y/ v.x);
}

bool satBoxCircle(const sf::RectangleShape& object1, const sf::CircleShape& object2)
{
        sf::Vector2f Points[4];
        Points[0] = object1.getTransform().transformPoint(0.f, 0.f);
        Points[1] = object1.getTransform().transformPoint(object1.getSize().x, 0.f);
        Points[2] = object1.getTransform().transformPoint(object1.getSize().x, object1.getSize().y);
        Points[3] = object1.getTransform().transformPoint(0.f, object1.getSize().y);
        sf::Vector2f circle_center(Object2.getPosition().x + object2.getRadius(), object2.getPosition().y + Object2.getRadius());
       
        //Find nearest square vertex to the circle center
        int index = 0;
        float min_distance = distance(Points[0], circle_center);
        for (int i = 0; i < 4; i++)
        {
                if (distance(Points[i], circle_center) < min_distance)
                {
                        min_distance = distance(Points[i], circle_center);
                        index = i;
                }
        }

        sf::Vector2f Axis[3] = {
                sf::Vector2f(Points[1].x - Points[0].x, Points[1].y - Points[0].y),
                sf::Vector2f(Points[1].x - Points[2].x, Points[1].y - Points[2].y),
                sf::Vector2f(Points[index].x - circle_center.x, Points[index].y - circle_center.y)
        };

       

        //Project each point into each axis and find min and max
        for (int i = 0; i < 3; i++)
        {
                float box_min = 0.f, box_max = 0.f;
                box_min = dotProduct(Points[0], Axis[i]);
                box_max = box_min;

                for (int j = 0; j<4; j++)
                {
                        float projection = dotProduct(Points[j], Axis[i]);
                        if (projection<box_min) box_min = projection;
                        if (projection>box_max) box_max = projection;
                }

                float circle_min = 0.f, circle_max = 0.f;
                sf::Vector2f point_perimeter1(circle_center.x - Object2.getRadius()*cos(findAngle(Axis[i])), circle_center.y - Object2.getRadius()*sin(findAngle(Axis[i])));
                sf::Vector2f point_perimeter2(circle_center.x + Object2.getRadius()*cos(findAngle(Axis[i])), circle_center.y  + Object2.getRadius()*sin(findAngle(Axis[i])));
               
                float circle_1_projection = dotProduct(point_perimeter1, Axis[i]);
                float circle_2_projection = dotProduct(point_perimeter2, Axis[i]);

                if (circle_1_projection < circle_2_projection)
                {
                        circle_min = circle_1_projection;
                        circle_max = circle_2_projection;
                }
                else
                {
                        circle_min = circle_2_projection;
                        circle_max = circle_1_projection;
                }

                if (!((circle_min <= box_max) && (circle_max >= box_min)))
                        return false;
        }
       
        return true;

}
 
« Last Edit: January 19, 2015, 08:25:56 pm by Carlitox »