【问题标题】:Resize a Rectangle which is on an angle调整一个角度上的矩形的大小
【发布时间】:2011-11-18 06:07:59
【问题描述】:

我有一个 Rectangle,它是一个 4 Point 结构的数组。它可以任意角度(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://www.assembla.com/code/moozhe-testing/subversion/nodes/rotateRectangle

【问题讨论】:

  • 向上面链接的 SVN 存储库添加了示例代码。它可以工作,除非在某些我无法弄清楚如何隔离的情况下。

标签: c# .net resize angle


【解决方案1】:

我看到了 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;
    }
}

【讨论】:

  • 你能详细说明 Normalize 扩展应该做什么吗?该函数返回应始终为 0,0 的“new Point()”。我错过了什么吗?
  • 假设你的意思是 Normalize() 函数通过将向量 x,y 除以长度来规范化向量,我已经能够让它工作但是我不知道如何处理长度为 0。
  • 大声笑,是的,它应该将向量转换为单位向量。我的错,我只是想删除我的 VS 中的红色:-P
  • @Moozhe :我找到了解决方案,所以我会更新我的解决方案。但是,如果您将 4 个点堆叠在一起,“OnMouseDown”中的“anchorPath.IsVisible(cursorPosition)”将始终返回 false,因此您将无法再次移动正方形:-S
  • @SaraSaeed:是的,delta = newpoint - oldpoint。你看到的第二件事实际上是一个错误。我经常编辑该代码以找到正确的答案,但我没有注意到该错误。感谢这些建设性的反馈!
猜你喜欢
  • 1970-01-01
  • 2017-09-13
  • 2020-10-07
  • 1970-01-01
  • 2011-09-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-16
相关资源
最近更新 更多