我正在尝试为以下 3D 立方体选择问题找到一种有效的算法:
想象一个 2D 点数组(让它大小为 x 大小的正方形)并将其称为边。
为了便于计算,让我们将 max 声明为 size-1 创建一个有六个边的立方体,在左下角保持 0,0,在右上角保持 max,max。使用 z 跟踪单个立方体所在的边,y 为上,x 为右
public class Point3D {
public int x,y,z;
public Point3D(){}
public Point3D(int X, int Y, int Z) {
x = X;
y = Y;
z = Z;
}
}
Point3D[,,] CreateCube(int size)
{
Point3D[,,] Cube = new Point3D[6, size, size];
for(int z=0;z<6;z++)
{
for(int y=0;y<size;y++)
{
for(int x=0;x<size;x++)
{
Cube[z,y,x] = new Point3D(x,y,z);
}
}
}
return Cube;
}
现在要选择一个随机单点,我们可以使用三个随机数,这样:
Point3D point = new Point(
Random(0,size), // 0 and max
Random(0,size), // 0 and max
Random(0,6)); // 0 and 5
要选择一个加号,我们可以检测给定方向是否适合当前边。否则,我们会发现立方体位于接触中心点的一侧。
使用 4 个函数,例如:
private T GetUpFrom<T>(T[,,] dataSet, Point3D point) where T : class {
if(point.y < max)
return dataSet[point.z, point.y + 1, point.x];
else {
switch(point.z) {
case 0: return dataSet[1, point.x, max]; // x+
case 1: return dataSet[5, max, max - point.x];// y+
case 2: return dataSet[1, 0, point.x]; // z+
case 3: return dataSet[1, max - point.x, 0]; // x-
case 4: return dataSet[2, max, point.x]; // y-
case 5: return dataSet[1, max, max - point.x];// z-
}
}
return null;
}
现在我想找到一种在随机点选择任意形状(如预定义的随机 blob)的方法。但会满足于将其调整为方形或锯齿形圆形。
实际的表面积会在拐角处弯曲并折叠到自身上,这很好,不需要补偿(想象一下在立方体的拐角上贴一个贴纸,如果拐角与贴纸的中心匹配,则需要四分之一的贴纸将其移除以使其粘在角落上并折叠)。这又是想要的效果。
不允许重复选择,因此需要以某种方式过滤将被选择两次的多维数据集(或以不发生重复的方式计算)。这可能很简单,例如使用 HashSet 或 List 并使用辅助函数来检查条目是否唯一(这很好,因为选择总是远低于最大 1000 个立方体)。
包含立方体边的类中此函数的委托如下所示: delegate T[] SelectShape(Point3D point, int size);
目前我正在考虑检查多维数据集的每一侧,以查看选择的哪一部分位于该侧。
计算选择的哪一部分位于所选 Point3D 的同一侧,因为我们不需要平移位置,只需平移边界即可。接下来是 5 次翻译,然后检查其他 5 面以查看所选区域的一部分是否在该面。
我在解决这样的问题方面变得生疏了,所以想知道是否有人有更好的解决方案来解决这个问题。
@arghbleargh 要求进一步解释:
我们将使用一个 6 面的立方体,大小为 16。每面是 16x16 点。存储为一个三维数组,我将 z 用于边、y、x,这样数组将使用:new Point3D[z,y,x] 启动,它对于锯齿状数组的工作方式几乎相同,默认情况下可序列化(所以那也很好)[z][y][x] 但需要单独初始化每个子数组。
让我们选择一个大小为 5x5 的正方形,以选定点为中心。要找到这样一个 5x5 的正方形减法并将 2 添加到相关轴:x-2 到 x+2 和 y-2 到 y+2。
随机选择一个边,我们选择的点是z = 0(立方体的x+边),y = 6,x = 6。
6-2 和 6+2 都很好地在边的 16 x 16 阵列的范围内,并且易于选择。
然而,将选择点移动到 x=0 和 y=6 将证明更具挑战性。因为 x - 2 需要查看我们选择的一侧的左侧。幸运的是,我们选择了边 0 或 x+,因为只要我们不在立方体的顶部或底部并且不进入立方体的顶部或底部,所有轴都是 x+ = 右,y+ = 上。因此,要获得左侧的坐标只需要减去 max (size - 1) - x。请记住 size = 16, max = 15, x = 0-2 = -2, max - x = 13。因此,这一边的小节将是 x = 13 到 15,y = 4 到 8。将其添加到我们的部分可以在原始一侧选择将给出整个选择。
将选择移至 0,6 会更复杂,因为现在我们无法隐藏在知道所有轴对齐的安全性之后。可能需要进行一些轮换。只有 4 种可能的翻译,所以它仍然是可管理的。
转移到 0,0 是问题真正开始出现的地方。现在左右两边都需要绕到另一边。此外,即使细分的部分也会有一个区域落在外面。唯一能治愈这个伤口的是我们不关心选择的重叠部分。因此,我们可以在可能的情况下跳过它们,也可以稍后从结果中过滤它们。
现在我们从“法线轴”一侧移动到底部,我们需要旋转并匹配正确的坐标,以便点正确地环绕边缘。
由于每边的轴都折叠在一个立方体中,因此某些轴可能需要翻转或旋转才能选择正确的点。
如果有更好的解决方案可以选择区域内立方体上的所有点,问题仍然存在。也许我可以给每一边一个平移矩阵并在世界空间中测试坐标?