2
\$\begingroup\$

I need some help, I have one vector in a rectangle like following

the vector in the square

(Ops, first A2 point on the top should be A1, assume it is A1)

Let's say the vector is like above It doesn't have intersection point actually, Basically, I need to find the point A1 and A2

I'm using cocos2d-x but framework specific solutions are not mandatory Thanks

\$\endgroup\$
  • \$\begingroup\$ Possible duplicate of Intersection of a Line and a Rectangle \$\endgroup\$ – Vaillancourt Jun 17 '16 at 21:51
  • \$\begingroup\$ Not necessarily a duplicate... In this case it's more like a projection. @FoxitcL Do you really mean a square or can it be a rectangle, like in the image? Is it always axis-aligned or can it be rotated? \$\endgroup\$ – 1000ml Jun 18 '16 at 13:18
  • \$\begingroup\$ Yes, Its always axis aligned and its rectangle. Updating the question sorry for the missing info \$\endgroup\$ – Can Tecim Jun 18 '16 at 17:07
  • \$\begingroup\$ How is your vector stored? Is it two points? A point and an offset? A point, an angle and a length? \$\endgroup\$ – HolyBlackCat Jun 18 '16 at 18:03
  • \$\begingroup\$ I store it with a point, angle and a length \$\endgroup\$ – Can Tecim Jun 18 '16 at 18:07
0
\$\begingroup\$

The goal is to project the vector onto the correct sides of the rectangle. Using the tangens function we're already halfway done. The rest is basic trigonometry.

Input data
Depending on how you store the rectangle, we have to transform the input data first. This method expects a rectangle that is defined solely by it's size (width w and height h). It's top left corner lies on the origin [0,0]. The Y-axis of the coordinate system expands downwards (1).

We expect the point p to always be inside the rectangle. The angle is defined in radians.

#include <cmath>
#include <utility>
#include <iostream>

struct Vec2
{
    float x, y;
    Vec2() : x(0), y(0) {}
    Vec2(float X, float Y) : x(X), y(Y) {}
};

typedef std::pair<Vec2, Vec2> Result;

// a  d
// b  c
struct Rect
{
    Vec2 a, b, c, d;
};

void project(const Rect& rect, Vec2 p, float angle, Result& output)
{
    float tanA = tan(angle);

    // Avoid division by zero
    if(tanA == 0)
    {
        output.first.x  = rect.d.x;
        output.first.y  = p.y;
        output.second.x = rect.a.x;
        output.second.y = p.y;
    }
    else
    {
        // Transform input (make p relative to rectangle)
        p.x -= rect.a.x;
        p.y -= rect.a.y;

        float w = rect.d.x - rect.a.x;
        float h = rect.b.y - rect.a.y;
        calcProjection(w, h, p, tanA, output);

        // Transform result back to original coordinates
        output.first.x  += rect.a.x;
        output.first.y  += rect.a.y;
        output.second.x += rect.a.x;
        output.second.y += rect.a.y;
    }
}


int main()
{
    Rect rect {
        Vec2(400, 600), // Top left
        Vec2(400, 700), // Bottom left
        Vec2(600, 700), // Bottom right
        Vec2(600, 600)  // Top right
    };

    Vec2 p(550, 650);
    float angle = -M_PI_4;

    Result result;
    project(rect, p, angle, result);

    std::cout << "A1: x=" << result.first.x  << " y=" << result.first.y  << std::endl;
    std::cout << "A2: x=" << result.second.x << " y=" << result.second.y << std::endl;
}

Projection Algorithm
The basic idea is like this:

  1. Use tan() and offsets to project p onto sides CD and AB. Result: yCD yAB
  2. Check for boundaries.
  3. If outside, project yCD and yAB onto DA or BC using trigonometry.

Projection on CD and DA

void calcProjection(float w, float h, const Vec2& p, float tanA, Result& output)
{
    float yCD = p.y - (tanA * (w - p.x));
    float yAB = p.y + (tanA * p.x);

    if(tanA < 0)
        tanA = -tanA;

    // First projection onto CD
    if(yCD < 0)
    {
        output.first.x = w + (yCD / tanA);
    }
    else if(yCD > h)
    {
        float opposite = yCD - h;
        output.first.x = w - (opposite / tanA);
        output.first.y = h;
    }
    else
    {
        output.first.x = w;
        output.first.y = yCD;
    }

    // Second projection onto AB
    if(yAB < 0)
    {
        output.second.x = -yAB / tanA;
    }
    else if(yAB > h)
    {
        float opposite  = yAB - h;
        output.second.x = opposite / tanA;
        output.second.y = h;
    }
    else
    {
        output.second.y = yAB;
    }
}

Note: The first result A1 will always be on the right side of the rectangle. A2 always on the left. If you want A1 to be on the front and A2 in the back of the vector, you can swap them when the following condition is true: angle >= PI/2 || angle <= -PI/2

I should also note that I haven't fully tested any of this.

(1) When using a coordinate system where the Y-axis expands upwards, simply negate the input angle: angle = -angle

\$\endgroup\$
  • \$\begingroup\$ Thanks for your detailed reply, will try it soon and let you know \$\endgroup\$ – Can Tecim Jun 20 '16 at 3:14
  • \$\begingroup\$ It works perfectly, I just completed porting it to cocos2dx. Thanks again :) \$\endgroup\$ – Can Tecim Jun 23 '16 at 21:10
  • \$\begingroup\$ I couldn't up-vote your answer btw, if you could approve my question I might fill the required reputation to vote \$\endgroup\$ – Can Tecim Jun 23 '16 at 21:18
  • \$\begingroup\$ Glad it worked. I already upvoted your Q. No problem, don't bother. \$\endgroup\$ – 1000ml Jun 24 '16 at 14:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.