调整有角度的矩形的大小

发布于 2024-12-05 05:59:47 字数 1133 浏览 0 评论 0原文

我有一个矩形,它是 4 点结构的数组。它可以以任何角度(0 到 360 度)旋转并正确绘制。

用户还可以拖动一个角来调整矩形的大小。例如,如果移动左下点,它也会更新左上点的 X 坐标和右下点的 Y 坐标。这样,无论移动到哪一点,它都将始终是一个矩形。

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;
}

在这里,我将四个点中的任何一个更改为给定的输入点 (newValue),然后修改链接的点,使其保持矩形形状。

但是,如果我的矩形处于这样的角度,我需要修改上面的代码才能工作:

http://thesaurus.maths .org/mmkb/media/png/Rectangle.png

此处添加的示例代码:

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

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:

http://thesaurus.maths.org/mmkb/media/png/Rectangle.png

Sample code added here:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

海夕 2024-12-12 05:59:47

我看到有 2 个解决方案。第一个理论上可行,但由于四舍五入,它最终不起作用。我将把第一个解决方案放在那里,但第二个是好的解决方案

在这些示例中,我将 4 个角称为 CornerA、B、C 和 D,以顺时针方式命名。假设您要将“CornerA”从位置 Point oldPoint 移动到位置 Point newPoint

第一个解决方案:

  1. 获取位置增量
  2. 在 Side sideAtoB 上对该增量进行投影,并将该向量添加到 PointD。
  3. 在 Side sideDtoA 上对该增量进行投影,并将该向量添加到 PointB。
  4. 将 PointA 设置为 newPoint。

第二种解决方案:

  1. 获取将对角连接到移动角的新位置的向量,我们称之为“对角线”。
  2. 将 B 的位置设置为“C + [sideAtoD 上的对角线投影]”。
  3. 将 D 的位置设置为“C + [sideAtoB 上的对角线投影]”。
  4. 将 PointA 设置为 newPoint。

这是第二个解决方案的代码:

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 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;
    }
}
弥枳 2024-12-12 05:59:47

我使用了一个计算垂直线之间交点的解决方案:

给定一个旋转矩形(未轴对齐),其中包含点 A、B、C 和 D,以及拖动点 D1(这是从 D 拖动的新点),则目标是找到 C1 和 A1 的新点。 B与D相对,不会移动。

要找到 C1 和 A1,请找到与点 D1 的线的交点,该线与 AB 和 BC 相交,并且垂直于线 AB 和 BC(直角)。这些交叉点将为您提供点 C1 和 A1。由于这两条线垂直于 AB 和 BC,因此它们将形成新的矩形。使用线性方程计算斜率、截距和倒数斜率,这将为您提供计算 AB 和 D1A 之间交点所需的两条直线。对另一边重复上述操作,得到 BC 和 D1C 之间的交集(记住,对于轴对齐的矩形,下面的内容不是必需的,而且更容易)

解决方案:
求直线 AB 和 AC 的斜率、相反的斜率和 y 截距。并找到线 D1A 和 D1C 的 y 截距。然后计算交集得到x,将x代入其中一条直线的线方程得到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;

相应地重复上述6次计算得到C1x和C1y

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文