Изменить размер прямоугольника, который находится под углом - PullRequest
1 голос
/ 16 сентября 2011

У меня есть Rectangle, который представляет собой массив из 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

1 Ответ

2 голосов
/ 16 сентября 2011

Я вижу 2 решения. Первый теоретически работает, но из-за округления он не работает. Я оставлю первое решение там, но второе - хорошее .

В этих примерах я назову 4 угла CornerA, B, C и D, названные по часовой стрелке. Допустим, вы перемещаете «CornerA» из позиции Point oldPoint в позицию Point newPoint.

Первое решение:

  1. Получить положение дельта
  2. Сделайте проекцию этой дельты на сторону sideAtoB и добавьте этот вектор в PointD.
  3. Сделайте проекцию этой дельты на сторону 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;
    }
}
...