开发者

Resize a Rectangle which is on an angle

开发者 https://www.devze.com 2023-04-05 00:26 出处:网络
I have a Rectangle which is an array of 4 Point structures.It can be rotated in place on any angle (0 to 360 degrees) and will draw properly.

I have a Rectangle which is an array of 4 Point structures. It can be rotated in place on any angle (0 to 360 degrees) and will draw properly.

The user can also drag a corner to resize the rectangle. For example, if they move the bottom-left point, it will also update the X coordinate of the upper-left point, and the Y coordinate of the lower-right point. In this way, it will always be a rectangle no matter which point they move开发者_运维百科.

Points[point] = newValue;

switch (point)
{
    case TopLeft:
        Points[BottomLeft].X = newValue.X;
        Points[TopRight].Y = newValue.Y;
        break;

    case BottomRight:
        Points[TopRight].X = newValue.X;
        Points[BottomLeft].Y = newValue.Y;
        break;

    case BottomLeft:
        Points[TopLeft].X = newValue.X;
        Points[BottomRight].Y = newValue.Y;
        break;

    case TopRight:
        Points[BottomRight].X = newValue.X;
        Points[TopLeft].Y = newValue.Y;
        break;
}

Here I change any of the four points to the given input point (newValue), and then modify the linked points so that it stays a Rectangle shape.

However, I need to modify the above code to work if my rectangle is on an angle like this:

Resize a Rectangle which is on an angle

Sample code added here:

http://www.assembla.com/code/moozhe-testing/subversion/nodes/rotateRectangle


I see 2 solutions. The first one theoretically works, but because of rounding, it ends up not working. I'll let the first solution there, but the second one is the good one.

In these samples, I'll call the 4 corners CornerA, B, C and D, named in a clockwise fashion. Let's say you're moving "CornerA" from a position Point oldPoint to position Point newPoint.

First solution :

  1. Get the position delta
  2. Do a projection of that delta on Side sideAtoB and add that vector to PointD.
  3. Do a projection of that delta on Side sideDtoA and add that vector to PointB.
  4. Set PointA to newPoint.

Second solution :

  1. Get the vector linking the opposite corner to the moving corner's new position, let's call it "Diagonal".
  2. Set B's position to "C + [Projection of Diagonal on sideAtoD].
  3. Set D's position to "C + [Projection of Diagonal on sideAtoB].
  4. Set PointA to newPoint.

Here is the code for that 2nd solution :

public class Rectangle
{
    // Obviously, one would need to assign values to these points.
    Point CornerA = new Point();
    Point CornerB = new Point();
    Point CornerC = new Point();
    Point CornerD = new Point();
    Dictionary<int, Point> points = new Dictionary<int, Point>();

    public Rectangle()
    {
        points.Add(0, CornerA);
        points.Add(1, CornerB);
        points.Add(2, CornerC);
        points.Add(3, CornerD);
    }

    public void MoveAPoint(int id, Point newPoint)
    {
        // Get the old point
        Point oldPoint = points[id];
        // Get the previous point
        Point pointPrevious = points[(id + 3) % 4];
        // Get the next point
        Point pointNext = points[(id + 1) % 4];
        // Get the opposite point
        Point pointOpposite = points[(id + 2) % 4];
        // Get the delta (variation) of the moving point
        Point delta = newPoint.Substract(oldPoint);

        // I call sides points, but they are actually vectors.
        // Get side from 'oldPoint' to 'pointPrevious'.
        Point sidePrevious = pointPrevious.Substract(oldPoint);

        // Get side from 'oldPoint' to 'pointNext'.
        Point sideNext = pointNext.Substract(oldPoint);

        // Get side from 'pointOpposite' to 'newPoint'.
        Point sideTransversal = newPoint.Substract(pointOpposite);

        PointF previousProjection;
        PointF nextProjection;

        if (sideNext.X == 0 && sideNext.Y == 0)
        {
            if (sidePrevious.X == 0 && sidePrevious.Y == 0)
            {
                return;
            }

            sideNext = new PointF(-sidePrevious.Y, sidePrevious.X);
        }
        else
        {
            sidePrevious = new PointF(-sideNext.Y, sideNext.X);
        }

        Point previousProjection = Projection(delta, sidePrevious);
        Point nextProjection = Projection(delta, sideNext);

        pointNext.SetToPoint(pointNext.AddPoints(previousProjection));
        pointPrevious.SetToPoint(pointPrevious.AddPoints(nextProjection));
        oldPoint.SetToPoint(newPoint);
    }

    private static Point Projection(Point vectorA, Point vectorB)
    {
        Point vectorBUnit = new Point(vectorB.X, vectorB.Y);
        vectorBUnit = vectorBUnit.Normalize();

        decimal dotProduct = vectorA.X * vectorBUnit.X + vectorA.Y * vectorBUnit.Y;
        return vectorBUnit.MultiplyByDecimal(dotProduct);
    }
}

public static class ExtendPoint
{
    public static Point Normalize(this Point pointA)
    {
        double length = Math.Sqrt(pointA.X * pointA.X + pointA.Y * pointA.Y);
        return new Point(pointA.X / length, pointA.Y / length);
    }

    public static Point MultiplyByDecimal (this Point point, decimal length)
    {
        return new Point((int)(point.X * length), (int)(point.Y * length));
    }

    public static Point AddPoints(this Point firstPoint, Point secondPoint)
    {
        return new Point(firstPoint.X + secondPoint.X, firstPoint.Y + secondPoint.Y);
    }

    public static Point Substract(this Point firstPoint, Point secondPoint)
    {
        return new Point(firstPoint.X - secondPoint.X, firstPoint.Y - secondPoint.Y);
    }

    public static void SetToPoint(this Point oldPoint, Point newPoint)
    {
        oldPoint.X = newPoint.X;
        oldPoint.Y = newPoint.Y;
    }
}


I used a solution that calculates the intersection between perpendicular lines:

Given a rotated rectangle (NOT axis aligned) with points, A, B, C, and D, and a dragged point D1 (which is the new point dragged from D), the goal is to find the new points for C1 and A1. B is opposite of D and will not move.

To find C1 and A1, find the intersection of the lines with points D1, which intersect with AB and BC and that are perpendicular to lines AB and BC (right angle). Those intersections will give you the points C1 and A1. Since the lines are perpendicular to AB and BC, they will form the new rectangle. Use the linear equations to calculate the slopes, intercepts and reciprocal slopes, which will give you both lines needed to calculate the intersection between AB and D1A. Repeat for the other side to get the intersection between BC and D1C (Remember, for axis-aligned rectangles, the below is not necessary and much easier)

Solution: Find the slopes, opposite reciprocal slopes, and y-intercepts of the lines AB and AC. And find the y-intercepts of the lines D1A and D1C. Then calculate the intersection to get x and plug x into the lienar equation of one of the lines to get y:

slope_AB = (B.y - A.y) / (B.x - A.x)
y_intercept_AB = B.y - slope_AB * B.x
reciprocal_slope_AB = -1 / slope_AB
y_intercept_AD1 = D1.y - reciprocal_slope_AB * D1.x
A1x = (y_intercept_AB - y_intercept_AD1) / (reciprocal_slope_AB - slope_AB);
A1y = (slope_AB * B1x) + y_intercept_AB;

Repeat above 6 calculations accordingly to get C1x and C1y

0

精彩评论

暂无评论...
验证码 换一张
取 消