比例翻译

时间:2014-12-15 12:08:26

标签: c# geometry system.drawing affinetransform

我希望通过执行旋转(围绕新原点)并将每个点转换为与其距离原点的当前距离成比例的量来更新点列表(PointF)(因此不是绝对的)译文)。

我目前依次针对每个点执行此操作,但是当移动多个点时性能很差。

我想让转换更有效率,所以想要使用矩阵。轮换没问题,但我不知道如何进行比例翻译。

我可以使用仿射矩阵吗?还有其他方法可以更有效地进行转换吗?

已更新

这是我目前的代码。我已经改变了一点,所以至少它确实使用矩阵进行旋转。请注意,翻译是基于比率的,因此更接近中心的点不会移动到更远的点:

    private void DragPointsAroundCentre(PointF centre, PointF priorLocation, PointF newLocation, PointF[] otherPoints)
    {
        // calculate the angle and length of the transformation from the original location
        var priorLength = Maths.Distance(centre, priorLocation);
        var newLength = Maths.Distance(centre, newLocation);

        var lengthRatio = newLength / priorLength;
        var rotationAngle = (float)Maths.Angle(centre, priorLocation, newLocation);

        // apply the rotation to the other points
        Rotate(otherPoints, rotationAngle, centre);

        // apply an equivalent translation to the other points
        for (int i = 0; i < otherPoints.Length ; i++)
        {
            var translation = GetPointOnLine(centre, otherPoints[i], (float) lengthRatio);
            otherPoints[i].X = translation.X;
            otherPoints[i].Y = translation.Y;
        }
    }

    private static void Rotate(PointF[] points, float angle, PointF center)
    {
        using (Matrix m = new Matrix())
        {
            m.RotateAt(angle, center);
            m.TransformPoints(points);
        }
    }

    // gets a point from a relative position on a line using the specified ratio
    private static PointF GetPointOnLine(PointF origin, PointF point, float ratio)
    {
        return new PointF(
            origin.X + (point.X - origin.X) * ratio,
            origin.Y + (point.Y - origin.Y) * ratio);
    }

1 个答案:

答案 0 :(得分:3)

这是我用于转换的代码。我希望这会对你有所帮助:

class Program
{
    static void Main(string[] args)
    {
        PointF[] points = new PointF[] 
        { 
            new PointF(1, 0), 
            new PointF(0, 1) 
        };

        float angle = 90; // in degrees
        PointF center = new PointF(1, 1);
        Rotate(points, angle, center);

        float offset = 10;
        PointF vector = new PointF(1, 1);
        Translate(points, offset, vector);
    }

    static void Rotate(PointF[] points, float angle, PointF center)
    {
        using (Matrix m = new Matrix())
        {
            m.RotateAt(angle, center);
            m.TransformPoints(points);
        }
    }

    // Translates point along the specified vector.
    static void Translate(PointF[] points, float offset, PointF vector)
    {
        float magnitude = (float)Math.Sqrt((vector.X * vector.X) + (vector.Y * vector.Y)); // = length
        vector.X /= magnitude;
        vector.Y /= magnitude;
        PointF translation = new PointF()
        {
            X = offset * vector.X,
            Y = offset * vector.Y
        };
        using (Matrix m = new Matrix())
        {
            m.Translate(translation.X, translation.Y);
            m.TransformPoints(points);
        }
    }
}

如果您需要将转换效率提高,则可以将两个转换矩阵合并为一个并仅转换所有点一次。

修改

您可以使用例如简单的并行循环来使其快一点。但即使是30.000.000点,在这种情况下差异也不是太大(我的情况是4个cpu核心)。但这当然取决于你处理它们的频率。

class Program
{
    static void Main(string[] args)
    {
        int pointCount = 30000000;
        PointF[] otherPoints = new PointF[pointCount];
        Random rnd = new Random();
        for (int i = 0; i < pointCount; i++)
        {
            otherPoints[i] = new Point(rnd.Next(), rnd.Next());
        }

        PointF centre = new PointF(3, 3);
        float lengthRatio = 7.3f;

        // apply an equivalent translation to the other points
        Stopwatch sw = new Stopwatch();

        sw.Start();
        for (int i = 0; i < otherPoints.Length; i++)
        {
            var translation = GetPointOnLine(centre, otherPoints[i], (float)lengthRatio);
            otherPoints[i].X = translation.X;
            otherPoints[i].Y = translation.Y;
        }
        sw.Stop();
        Console.WriteLine("Single thread: {0} sec.", sw.Elapsed.TotalSeconds);

        sw.Reset();
        sw.Start();
        Parallel.For(0, pointCount, i =>
        {
            var translation = GetPointOnLine(centre, otherPoints[i], (float)lengthRatio);
            otherPoints[i].X = translation.X;
            otherPoints[i].Y = translation.Y;

        });
        sw.Stop();
        Console.WriteLine("Multi thread: {0} sec.", sw.Elapsed.TotalSeconds);
        Console.ReadKey();
    }

    // gets a point from a relative position on a line using the specified ratio
    private static PointF GetPointOnLine(PointF origin, PointF point, float ratio)
    {
        return new PointF(
            origin.X + (point.X - origin.X) * ratio,
            origin.Y + (point.Y - origin.Y) * ratio);
    }
}

修改-2

我发现了一个与您的变换非常相似的变换,并使用单个矩阵仅在一个循环中变换点。这是旧转换和新转换的代码:

class Program
{
    static void Main(string[] args)
    {
        PointF[] points1 = new PointF[] 
        { 
            new PointF(1f, 0f),
            new PointF(0f, 1f),
            new PointF(1f, 1f),
            new PointF(2f, 2f),
        };
        PointF[] points2 = new PointF[]
        { 
            new PointF(1f, 0f),
            new PointF(0f, 1f),
            new PointF(1f, 1f),
            new PointF(2f, 2f),
        };

        PointF center = new PointF(2f, 2f);

        float priorLength = 4f;
        float newLength = 5f;

        float lengthRatio = newLength / priorLength;

        float rotationAngle = 45f;

        Transformation_old(points1, rotationAngle, center, lengthRatio);
        Transformation_new(points2, rotationAngle, center, lengthRatio);

        Console.ReadKey();
    }

    static void Transformation_old(PointF[] points, float rotationAngle, PointF center, float lengthRatio)
    {
        Rotate(points, rotationAngle, center);

        for (int i = 0; i < points.Length; i++)
        {
            var translation = GetPointOnLine(center, points[i], lengthRatio);
            points[i].X = translation.X;
            points[i].Y = translation.Y;
        }
    }

    static void Rotate(PointF[] points, float angle, PointF center)
    {
        using (Matrix m = new Matrix())
        {
            m.RotateAt(angle, center);
            m.TransformPoints(points);
        }
    }

    private static PointF GetPointOnLine(PointF origin, PointF point, float ratio)
    {
        return new PointF(
            origin.X + (point.X - origin.X) * ratio,
            origin.Y + (point.Y - origin.Y) * ratio);
    }

    // Uses only a single matrix and a single transformation:
    static void Transformation_new(PointF[] points, float rotationAngle, PointF center, float lengthRatio)
    {
        using (Matrix m = new Matrix())
        {
            m.RotateAt(rotationAngle, center, MatrixOrder.Prepend);

            // Replaces GetPointOnLine
            m.Translate(center.X, center.Y, MatrixOrder.Prepend);
            m.Scale(lengthRatio, lengthRatio, MatrixOrder.Prepend);
            m.Translate(-center.X, -center.Y, MatrixOrder.Prepend);

            m.TransformPoints(points);
        }
    }
}