0

我在位于原点 (0,0,0) 的 3d 数组中有一个二进制对象。我需要在 z 轴上动态地旋转这个对象。如何以任何角度旋转没有固定大小的多维数组?

我创建了一个 3d 点类:

public class Point3
{
    public double X { get; set; }
    public double Y { get; set; }
    public double Z { get; set; }
}

我正在考虑在每个点上做一个 foreach 并旋转它:

foreach (Point3 p in listPoint3)
{
    RotatePoint(p, angle);
}

有什么建议吗?

4

3 回答 3

4

您可以创建一个例程来使用参数方程旋转每个点以旋转 3d 对象。

x' = x*cos(o)-y*sin(o)

y' = y*sin(o)-y*cos(o)

z' = z

private Point3 RotatePoint(Point3 p0, int angle)
{
    Point3 p =  new Point3()
    {
        X = p0.X * Math.Cos(angle) - p0.Y * Math.Sin(angle),
        Y = p0.X * Math.Sin(angle) + p0.Y * Math.Cos(angle),
        Z = p0.Z,
    };

    return p;
}
于 2013-09-30T14:36:32.003 回答
2

您需要知道要在哪个轴上旋转。但如果这只是一个看哪里的问题。(命名空间 System.Windows.Media.Media3D)

你可以试试这个:

double angle = 45;

RotateTransform3D zrotation = new RotateTransform3D(new AxisAngleRotation3D(
                                  new Vector3D(0, 0, 1), angle));

foreach (Point3D p in listPoint3)
{
    Point3D rotatedPoint = zrotation.Transform(p);
}

你应该使用内置Point3D

另外,如果您想堆叠这些:(多个转换)

double zAngle = 45;
double xAngle = 10;

Transform3DGroup group = new Transform3DGroup();
group.Children.Add( new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(1, 0, 0), xAngle)));
group.Children.Add( new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(0, 0, 1), zAngle)));

foreach (Point3D p in listPoint3)
{
    Point3D rotatedPoint = group.Transform(p);
}
于 2013-09-30T14:34:42.027 回答
1

所以你有一个“单色对象”,它像 3D 位图一样存储,你想绕 Z 轴旋转。您必须首先了解,经过多次旋转后,您最终会出现光学像差,这是因为您使用的array index是自然数来表示对象组件的坐标。

旋转后,任何整数值都极有可能变成无理数。传统上(不讨论特殊程序和框架)人们将无理数的近似值存储在doubleorfloatdecimal变量(只能存储有理数集的一小部分)与通过存储无理数的近似值相比毫无意义在一个整数 (an array index) 中。

此外,即使质量损失在您的应用中并不重要,您也必须确保您理解从数学上讲,经过多次旋转后,您的 3d 形状将被原始中刻有的圆柱体修剪平行六面体,沿 Z 轴。

它是这样的。您说您已经创建了一个名为Point3

public class Point3 {
    public double X { get; set; }
    public double Y { get; set; }
    public double Z { get; set; }
}

也许您应该遵循@Jeroen van Langen 的建议并使用标准类,如果这样的类已经存在的话。这样做的好处是,如果有人已经构建或将构建使用该类的库,您可以立即开始使用该库。但这不是现在那么重要。

@Alpert 已经提供了一个很棒的 C# 代码,用于围绕 oZ 轴旋转一个点。这是该代码的 n“扩展方法”改编:

public static class RotationHelpers {

    public static Point3 RotatePoint(this Point3 point, int angle) {
        var result = new Point3() {
            X = point.X * Math.Cos(angle) - point.Y * Math.Sin(angle),
            Y = point.X * Math.Sin(angle) + point.Y * Math.Cos(angle),
            Z = point.Z,
        };
        return result;
    }

    ...
}

您可以更进一步,制作一个围绕 oZ 轴旋转一系列点的扩展方法:

public static class RotationHelpers {

    ...

    public static IEnumerable<Point3> RotatePoints(this IEnumerable<Point3> points, int angle) {
        foreach (var point in points)
            yield return point.RotatePoint(angle);
    }

    ...

}

现在您说您有一个 3d 原始矩阵,其中包含 1 和 0:

int[,,] matrix;

您需要以某种方式将该矩阵中固有定义的点转换为Point3实例序列,旋转这些实例,然后将结果序列转换回int[,,]矩阵。

可以像这样实现(记住我之前谈到的质量损失):

public static class RotationHelpers {

    ...

    public static IEnumerable<Point3> ToPoints(this int[,,] matrix) {
        int lx = matrix.GetLength(0);
        int ly = matrix.GetLength(1);
        int lz = matrix.GetLength(2);

        for (int x = 0; x < lx; x++)
        for (int y = 0; y < ly; y++)
        for (int z = 0; z < lz; z++) {

            bool is1 = matrix[x, y, z] != 0;    
            if (is1)
                yield return new Point3 {
                    X = x - lx / 2,
                    Y = y - ly / 2,
                    Z = z - lz / 2
                };

        }
    }

    ...

}

这将采用 WIDTH x HEIGHT x DEPTH 矩阵中的所有单元格,并且对于每个不等于 0 的单元格,它将产生一个Point3具有该特定位置坐标的新实例。

然后可以使用前面描述的方法将该序列旋转一个角度RotatePoints,然后可以使用以下方法将生成的Point3实例序列“渲染”回数组中:

public static class RotationHelpers {

    ...

    public static void AssignPoints(this int[,,] matrix, IEnumerable<Point3> points) {
        int lx = matrix.GetLength(0);
        int ly = matrix.GetLength(1);
        int lz = matrix.GetLength(2);

        for (int x = 0; x < lx; x++)
        for (int y = 0; y < ly; y++)
        for (int z = 0; z < lz; z++)
            matrix[x, y, z] = 0;

        foreach (var point in points) {
            // this is when quality is lost, because things like 1.7 and 1.71
            // will both become =2

            var x = (int)Math.Round(point.X) + lx / 2;
            var y = (int)Math.Round(point.Y) + ly / 2;
            var z = (int)Math.Round(point.Z) + lz / 2;

            // this is where you loose parts of the object because
            // it doesn't fit anymore inside the parallelepiped
            if ((x >= 0) && (y >= 0) && (z >= 0) &&
                (x < lx) && (y < ly) && (z < lz))
                matrix[x, y, z] = 1;
        }
    }

    ...

}

总结一下,您可以使用所有这些方法,如下所示:

int[,,] matrix = ...
int angle = ...

IEnumerable<Point3> points = matrix.ToPoints();
IEnumerable<Point3> rotatedPoints = points.RotatePoints(angle);

matrix.AssignPoints(rotatedPoints);
// now you have the original matrix, rotated by angle
于 2013-09-30T15:32:59.740 回答