我不相信在到达两极后在每一层上运行中点圆算法会产生所需的结果,因为在 LED 未点亮的表面上会有间隙。但是,这可能会给出您想要的结果,因此这取决于美学。这篇文章是基于使用中点圆算法来确定通过中间两个垂直八分圆的层的半径,然后在绘制每个圆时同时设置极坐标八分圆的点。
我认为基于@Nick Udall 的评论和回答here,使用圆形算法来确定水平切片的半径将适用于我在对他的回答的评论中提出的修改。应该修改圆算法以将初始误差作为输入,并绘制极坐标八分圆的附加点。
- 在
y0 + y1和y0 - y1处绘制标准圆算法点:x0 +/- x, z0 +/- z, y0 +/- y1,x0 +/- z, z0 +/- x, y0 +/- y1,共16个点。这构成了球体的大部分垂直方向。
- 另外画点
x0 +/- y1, z0 +/- x, y0 +/- z和x0 +/- x, z0 +/- y1, y0 +/- z,共16个点,形成球体的极冠。
通过将外部算法的误差传递给圆算法,它将允许对每一层的圆进行子体素调整。在不将误差传递给内部算法的情况下,圆的赤道将近似为一个圆柱体,x、y、z 轴上的每个近似球面将形成一个正方形。包括误差在内,给定足够大半径的每个面将近似为一个实心圆。
以下代码修改自维基百科的Midpoint circle algorithm。 DrawCircle 算法将命名法更改为在 xz 平面中运行,添加了第三个初始点 y0、y 偏移量 y1 和初始误差 error0。 DrawSphere 是从同一个函数修改而来的,以获取第三个初始点 y0 并调用 DrawCircle 而不是 DrawPixel
public static void DrawCircle(int x0, int y0, int z0, int y1, int radius, int error0)
{
int x = radius, z = 0;
int radiusError = error0; // Initial error state passed in, NOT 1-x
while(x >= z)
{
// draw the 32 points here.
z++;
if(radiusError<0)
{
radiusError+=2*z+1;
}
else
{
x--;
radiusError+=2*(z-x+1);
}
}
}
public static void DrawSphere(int x0, int y0, int z0, int radius)
{
int x = radius, y = 0;
int radiusError = 1-x;
while(x >= y)
{
// pass in base point (x0,y0,z0), this algorithm's y as y1,
// this algorithm's x as the radius, and pass along radius error.
DrawCircle(x0, y0, z0, y, x, radiusError);
y++;
if(radiusError<0)
{
radiusError+=2*y+1;
}
else
{
x--;
radiusError+=2*(y-x+1);
}
}
}
对于半径为 4 的球体(实际上需要 9x9x9),这将运行 DrawCircle 例程的三次迭代,第一次绘制一个典型的半径为 4 的圆(三个步骤),第二次绘制一个半径为 4 的圆初始误差为 0(也是三个步骤),然后第三个绘制一个半径为 3 的圆,初始误差为 0(也是三个步骤)。最终得到 9 个计算点,每个点绘制 32 个像素。
这使得 32(每个圆的点)x 3(每个点的加法或减法操作)+ 6(每次迭代的加法、减法、移位操作)=每个计算点的 102 次加法、减法或移位操作。在此示例中,每个圆圈 3 个点 = 每层 306 次操作。半径算法还每层增加了 6 次操作,迭代 3 次,所以 306 + 6 * 3 = 936 基本算术运算对于示例半径为 4。
这里的代价是您将重复设置一些像素而无需额外的条件检查(即 x = 0、y = 0 或 z = 0),因此如果您的 I/O 很慢,您最好添加条件检查。假设所有 LED 在开始时都被清除,示例圆圈将设置 288 个 LED,而由于重复设置,实际点亮的 LED 会少得多。
对于适合 8x8x8 网格的所有球体,这看起来比蛮力法的表现要好,但蛮力法无论半径如何都具有一致的时序,而在绘制大半径球体时,这种方法会减慢速度,其中只有部分将被显示。然而,随着显示立方体分辨率的提高,该算法时序将保持一致,而蛮力将增加。