Ievgen
Ievgen

Reputation: 4443

Get intersection point of rectangle and line

I need get intersection point of rectangle and line. I have point B inside rectangle(center of rectangle) and have point A outside. And i need to find point C on one of rectangle borders. Also I get width and height of rectangle.

enter image description here

All this will be WPF application, so if any build in functions i will be very happy.

Upvotes: 9

Views: 12193

Answers (6)

Maari
Maari

Reputation: 51

Line Intersection Possibilities in Rectangle

Hope It works 100%

I am also had this same problem. So after two days of hard effort finally I created this method,

Main method,

    // Tuple<entryPoint, exitPoint, lineStatus>
    private Tuple<Point, Point, Line> GetIntersectionPoint(Point a, Point b, Rectangle rect)
    {
        if (IsWithinRectangle(a, rect) && IsWithinRectangle(b, rect))
        {
            // Can't set null to Point that's why I am returning just empty object
            return new Tuple<Point, Point, Line>(new Point(), new Point(), Line.InsideTheRectangle);
        }
        else if (!IsWithinRectangle(a, rect) && !IsWithinRectangle(b, rect))
        {
            if (!LineIntersectsRectangle(a, b, rect))
            {
                // Can't set null to Point that's why I am returning just empty object
                return new Tuple<Point, Point, Line>(new Point(), new Point(), Line.NoIntersection);
            }

            Point entryPoint = new Point();
            Point exitPoint = new Point();

            bool entryPointFound = false;

            // Top Line of Chart Area
            if (LineIntersectsLine(a, b, new Point(0, 0), new Point(rect.Width, 0)))
            {
                entryPoint = GetPointFromYValue(a, b, 0);
                entryPointFound = true;
            }
            // Right Line of Chart Area
            if (LineIntersectsLine(a, b, new Point(rect.Width, 0), new Point(rect.Width, rect.Height)))
            {
                if (entryPointFound)
                    exitPoint = GetPointFromXValue(a, b, rect.Width);
                else
                {
                    entryPoint = GetPointFromXValue(a, b, rect.Width);
                    entryPointFound = true;
                }
            }
            // Bottom Line of Chart
            if (LineIntersectsLine(a, b, new Point(0, rect.Height), new Point(rect.Width, rect.Height)))
            {
                if (entryPointFound)
                    exitPoint = GetPointFromYValue(a, b, rect.Height);
                else
                {
                    entryPoint = GetPointFromYValue(a, b, rect.Height);
                }
            }
            // Left Line of Chart
            if (LineIntersectsLine(a, b, new Point(0, 0), new Point(0, rect.Height)))
            {
                exitPoint = GetPointFromXValue(a, b, 0);
            }

            return new Tuple<Point, Point, Line>(entryPoint, exitPoint, Line.EntryExit);
        }
        else
        {
            Point entryPoint = GetEntryIntersectionPoint(rect, a, b);
            return new Tuple<Point, Point, Line>(entryPoint, new Point(), Line.Entry);
        }
    }

Supporting methods,

    enum Line
    {
        // Inside the Rectangle so No Intersection Point(Both Entry Point and Exit Point will be Null)
        InsideTheRectangle,

        // One Point Inside the Rectangle another Point Outside the Rectangle. So it has only Entry Point
        Entry,

        // Both Point Outside the Rectangle but Intersecting. So It has both Entry and Exit Point
        EntryExit,

        // Both Point Outside the Rectangle and not Intersecting. So doesn't has both Entry and Exit Point
        NoIntersection
    }

    private Point GetEntryIntersectionPoint(Rectangle rect, Point a, Point b)
    {
        // For top line of the rectangle
        if (LineIntersectsLine(new Point(0, 0), new Point(rect.Width, 0), a, b))
        {
            return GetPointFromYValue(a, b, 0);
        }
        // For right side line of the rectangle
        else if (LineIntersectsLine(new Point(rect.Width, 0), new Point(rect.Width, rect.Height), a, b))
        {
            return GetPointFromXValue(a, b, rect.Width);
        }
        // For bottom line of the rectangle
        else if (LineIntersectsLine(new Point(0, rect.Height), new Point(rect.Width, rect.Height), a, b))
        {
            return GetPointFromYValue(a, b, rect.Height);
        }
        // For left side line of the rectangle
        else
        {
            return GetPointFromXValue(a, b, 0);
        }
    }

    public bool LineIntersectsRectangle(Point p1, Point p2, Rectangle r)
    {
        return LineIntersectsLine(p1, p2, new Point(r.X, r.Y), new Point(r.X + r.Width, r.Y)) ||
               LineIntersectsLine(p1, p2, new Point(r.X + r.Width, r.Y), new Point(r.X + r.Width, r.Y + r.Height)) ||
               LineIntersectsLine(p1, p2, new Point(r.X + r.Width, r.Y + r.Height), new Point(r.X, r.Y + r.Height)) ||
               LineIntersectsLine(p1, p2, new Point(r.X, r.Y + r.Height), new Point(r.X, r.Y)) ||
               (r.Contains(p1) && r.Contains(p2));
    }

    private bool LineIntersectsLine(Point l1p1, Point l1p2, Point l2p1, Point l2p2)
    {
        float q = (l1p1.Y - l2p1.Y) * (l2p2.X - l2p1.X) - (l1p1.X - l2p1.X) * (l2p2.Y - l2p1.Y);
        float d = (l1p2.X - l1p1.X) * (l2p2.Y - l2p1.Y) - (l1p2.Y - l1p1.Y) * (l2p2.X - l2p1.X);

        if (d == 0)
        {
            return false;
        }

        float r = q / d;

        q = (l1p1.Y - l2p1.Y) * (l1p2.X - l1p1.X) - (l1p1.X - l2p1.X) * (l1p2.Y - l1p1.Y);
        float s = q / d;

        if (r < 0 || r > 1 || s < 0 || s > 1)
        {
            return false;
        }

        return true;
    }

    // For Large values, processing with integer is not working properly
    // So I here I am dealing only with double for high accuracy
    private Point GetPointFromYValue(Point a, Point b, double y)
    {
        double x1 = a.X, x2 = b.X, y1 = a.Y, y2 = b.Y;
        double x = (((y - y1) * (x2 - x1)) / (y2 - y1)) + x1;
        return new Point((int)x, (int)y);
    }

    // For Large values, processing with integer is not working properly
    // So here I am dealing only with double for high accuracy
    private Point GetPointFromXValue(Point a, Point b, double x)
    {
        double x1 = a.X, x2 = b.X, y1 = a.Y, y2 = b.Y;
        double y = (((x - x1) * (y2 - y1)) / (x2 - x1)) + y1;
        return new Point((int)x, (int)y);
    }

    // rect.Contains(point) is not working properly in some cases.
    // So here I created my own method
    private bool IsWithinRectangle(Point a, Rectangle rect)
    {
        return a.X >= rect.X && a.X <= rect.X + rect.Width && a.Y >= rect.Y && a.Y <= rect.Y + rect.Height;
    }

Upvotes: 1

Ievgen
Ievgen

Reputation: 4443

Solution for C#, WPF:

 /// <summary>
    /// Get Intersection point
    /// </summary>
    /// <param name="a1">a1 is line1 start</param>
    /// <param name="a2">a2 is line1 end</param>
    /// <param name="b1">b1 is line2 start</param>
    /// <param name="b2">b2 is line2 end</param>
    /// <returns></returns>
    public static Vector? Intersects(Vector a1, Vector a2, Vector b1, Vector b2)
    {
        Vector b = a2 - a1;
        Vector d = b2 - b1;
        var bDotDPerp = b.X * d.Y - b.Y * d.X;

        // if b dot d == 0, it means the lines are parallel so have infinite intersection points
        if (bDotDPerp == 0)
            return null;

        Vector c = b1 - a1;
        var t = (c.X * d.Y - c.Y * d.X) / bDotDPerp;
        if (t < 0 || t > 1)
            {
            return null;
        }

        var u = (c.X * b.Y - c.Y * b.X) / bDotDPerp;
        if (u < 0 || u > 1)
        {
            return null;
        }

        return a1 + t * b;
    }

Edit Found Link to SO question where the answer above comes from.

Upvotes: 4

Sjoerd C. de Vries
Sjoerd C. de Vries

Reputation: 16232

With ax and ay the coordinates of A, and bx, by the coordinates of B, and assuming the centre of the rectangle with width w and height h is at {0,0} the following should work

IntersectionRectangleLine[{ax_, ay_}, {bx_, by_}, h_, w_] :=
  Module[{\[Mu]r, \[Mu]l, \[Mu]t, \[Mu]b},
    {\[Mu]r, \[Mu]l, \[Mu]t, \[Mu]b} = {-((-2 ay bx + 2 ax by - ax w + 
      bx w)/((ay - by) h)), -((-2 ay bx + 2 ax by + ax w - 
      bx w)/((ay - by) h)), -((
     2 ay bx - 2 ax by - ay h + by h)/((ax - bx) w)), -((
     2 ay bx - 2 ax by + ay h - by h)/((ax - bx) w))};
 Which[
   -1 <= \[Mu]r <= 1, {0, w/2} + \[Mu]r {h/2, 0},
   -1 <= \[Mu]l <= 1, {0, -w/2} + \[Mu]l {h/2, 0},
   -1 <= \[Mu]t <= 1, {h/2, 0} + \[Mu]t {0, w/2},
   -1 <= \[Mu]b <= 1, {-h/2, 0} + \[Mu]b {0, w/2}
 ]
]

This based on the solutions for the intersection of the four lines making up the triangle

  In[114]:= Solve[Thread[\[Lambda] ({bx, by} - {ax, ay}) + {ax, ay} == {0, w/2} + \[Mu] {h/2, 0}], \[Mu], {\[Lambda]}]

 Out[114]= {{\[Mu] -> -((-2 ay bx + 2 ax by - ax w + bx w)/((ay - by) h))}}

(top line as an example here).

And for Evgeny, this is how it looks on my screen. Quite a lot more readable.

prettier version of code

Upvotes: 2

msarchet
msarchet

Reputation: 15232

If you know the dimensions of the rectangle, which I assume you do"

  • rX rectangle width
  • rY rectangle height
  • Ay A's Y Position
  • Ax A's X Position
  • By B's Y Position
  • Bx B's X Position
  • Cy C's Y Position
  • Cx C's X Position

Cy = By + rY / 2

The C Position is at the top of the rectangle, so it is the By position + half of the rY position

Then we just need to calculate the Cxposition.

Cx = (Bx + ((Ax - Bx) / (Ay - By)) * Cy)

You can get the X and Y Coordiantes for A and B by using the Point

Upvotes: 1

James Sumners
James Sumners

Reputation: 14775

Without knowing WPF, or any of its functions, this is how I would do it:

  1. Create a temporary point D that creates a right angle between B and C.
  2. The length of CD should be known since B is at the center of the rectangle. Therefore, it should be simple to compute the length of BD.
  3. Determine the length of BC by sqrt( (BD)^2 + (CD)^2 ).
  4. Given the position of A, you know if C is before or after the midpoint of the rectangle's side. Therefore, you can use the length of BC to calculate the position of C on the side.

Upvotes: 3

BrokenGlass
BrokenGlass

Reputation: 160852

This is basic math solving line-line intersection, check out topcoder for a tutorial:

Line-Line Intersection One of the most common tasks you will find in geometry problems is line intersection. Despite the fact that it is so common, a lot of coders still have trouble with it. The first question is, what form are we given our lines in, and what form would we like them in? Ideally, each of our lines will be in the form Ax+By=C, where A, B and C are the numbers which define the line. However, we are rarely given lines in this format, but we can easily generate such an equation from two points. Say we are given two different points, (x1, y1) and (x2, y2), and want to find A, B and C for the equation above. We can do so by setting A = y2-y1 B = x1-x2 C = A*x1+B*y1

Upvotes: 4

Related Questions