开发者

Correcting 3D line drawing in C#

开发者 https://www.devze.com 2023-01-30 11:28 出处:网络
I have (with assistance) created a function that plots and draws a line of blocks within a 3D space. Generally this is performed in a 64x64x64 gridded cube.

I have (with assistance) created a function that plots and draws a line of blocks within a 3D space. Generally this is performed in a 64x64x64 gridded cube.

This is the code I have:

    internal static int DrawLine(Player theplayer, Byte drawBlock,
                                 int x0, int y0, int z0, int x1, int y1, int z1)
    {
        int blocks = 0;
        bool cannotUndo = false;
        bool detected = false;

        int dx = x1 - x0;
        int dy = y1 - y0;
        int dz = z1 - z0;

        DrawOneBlock(theplayer, drawBlock, x0, y0, z0, ref blocks, ref cannotUndo);
        if (Math.Abs(dx) > Math.Abs(dy) &&
            Math.Abs(dx) > Math.Abs(dz) && 
            detected == false)
        {
            detected = true;
            float my = (float)dy / (float)dx;
            float mz = (float)dz / (float)dx;
            float by = y0 - my * x0;
            float bz = z0 - mz * x0;
            dx = (dx < 0) ? -1 : 1;
            while (x0 != x1)
            {
                x0 += dx;
                DrawOneBlock(theplayer, drawBlock,
                    Convert.ToInt32(x0),
                    Convert.ToInt32(Math.Round(my * x0 + by)),
                    Convert.ToInt32(Math.Round(mz * x0 + bz)),
                    ref blocks, ref cannotUndo);
            }
        }
        if (Math.Abs(dy) > Math.Abs(dz) && 
            Math.Abs(dy) > Math.Abs(dx) &&
            detected == false)
        {
            detected = true;
            float mz = (float)dz / (float)dy;
            float mx = (float)dx / (float)dy;
            float bz = z0 - mz * y0;
            float bx = x0 - mx * y0;
            dy = (dy < 0) ? -1 : 1;
            while (y0 != y1)
            {
                y0 += dy;
                DrawOneBlock(theplayer, drawBlock,
                             Convert.ToInt32(Math.Round(mx * y0 + bx)),
                             Convert.ToInt32(y0),
                             Convert.ToInt32(Math.Round(mz * y0 + bz)),
                             ref blocks, ref cannotUndo);
            }
        }
        if (detected == false)
        {
            detected = true;
            float mx = (float)dx / (float)dz;
            float my = (float)dy / (float)dz;
            float bx = x0 - mx * z0;
            float by = y0 - my * z0;
            dz = (dz < 0) ? -1 : 1;
            while (z0 != z1)
            {
                z0 += dz;
                DrawOneBlock(theplayer, drawBlock,
                             Convert.ToInt32(Math.Round(mx * z0 + bx)),
                             Convert.ToInt32(Math.Round(my * z0 + by)),
                             Convert.ToInt32(z0),
                             ref blocks, ref cannotUndo);
      开发者_开发问答      }
        }
        return blocks;
    }

It should queue up the block drawing and return the number of blocks it has drawn. The problem is that it is not drawing an un-broken line. In certain instances it leaves gaps between the blocks when at the very least all blocks should be connected by their vertices.

The only part of the code I struggled with is that I was calculating the largest difference in axis and creating a slope constant. I ran into an issue when trying to do a perfect diagonal line. All values were equal so I just defaulted to the z axis - this is where I believe the issue exists.


Maybe the Bresenham line algorithm modified to (hopefully) work in 3D could be an alternative for you?

public static void Swap<T>(ref T x, ref T y)
{
    T tmp = y;
    y = x;
    x = tmp;
}

private void Draw3DLine(int x0, int y0, int z0, int x1, int y1, int z1)
{
    bool steepXY = Math.Abs(y1 - y0) > Math.Abs(x1 - x0);
    if (steepXY) { Swap(ref x0, ref y0); Swap(ref x1, ref y1); }

    bool steepXZ = Math.Abs(z1 - z0) > Math.Abs(x1 - x0);
    if (steepXZ) { Swap(ref x0, ref z0); Swap(ref x1, ref z1); }

    int deltaX = Math.Abs(x1 - x0);
    int deltaY = Math.Abs(y1 - y0);
    int deltaZ = Math.Abs(z1 - z0);

    int errorXY = deltaX / 2, errorXZ = deltaX / 2;

    int stepX = (x0 > x1) ? -1 : 1;  
    int stepY = (y0 > y1) ? -1 : 1;
    int stepZ = (z0 > z1) ? -1 : 1;

    int y=y0, z=z0;

    // Check if the end of the line hasn't been reached.
    for(int x = x0; x!=x1; x+=stepX) 
    {
        int xCopy=x, yCopy=y, zCopy=z;

        if (steepXZ) Swap(ref xCopy, ref zCopy);
        if (steepXY) Swap(ref xCopy, ref yCopy);

        // Replace the WriteLine with your call to DrawOneBlock
        Console.WriteLine("[" + xCopy + ", " + yCopy + ", " + zCopy + "], ");

        errorXY -= deltaY;
        errorXZ -= deltaZ;

        if (errorXY < 0) 
        {
            y += stepY;
            errorXY += deltaX;
        }

        if (errorXZ < 0) 
        {
            z += stepZ;
            errorXZ += deltaX;
        }
    }
}


internal static void LineCallback(Player player, Position[] marks, object tag) //MODIFIED//
    {
        byte drawBlock = (byte)tag;
        if (drawBlock == (byte)Block.Undefined)
        {
            drawBlock = (byte)player.lastUsedBlockType;
        }

        player.undoBuffer.Clear();

        int blocks = 0;
        bool cannotUndo = false;

        // LINE CODE

        int x1 = marks[0].x, y1 = marks[0].y, z1 = marks[0].h, x2 = marks[1].x, y2 = marks[1].y, z2 = marks[1].h;
        int i, dx, dy, dz, l, m, n, x_inc, y_inc, z_inc, err_1, err_2, dx2, dy2, dz2;
        int[] pixel = new int[3];
        pixel[0] = x1;
        pixel[1] = y1;
        pixel[2] = z1;
        dx = x2 - x1;
        dy = y2 - y1;
        dz = z2 - z1;
        x_inc = (dx < 0) ? -1 : 1;
        l = Math.Abs(dx);
        y_inc = (dy < 0) ? -1 : 1;
        m = Math.Abs(dy);
        z_inc = (dz < 0) ? -1 : 1;
        n = Math.Abs(dz);
        dx2 = l << 1;
        dy2 = m << 1;
        dz2 = n << 1;

        DrawOneBlock(player, drawBlock, x2, y2, z2, ref blocks, ref cannotUndo);
        DrawOneBlock(player, drawBlock, x2, y2, z2, ref blocks, ref cannotUndo);

        if ((l >= m) && (l >= n)) {

            err_1 = dy2 - l;
            err_2 = dz2 - l;
            for (i = 0; i < l; i++) {
                DrawOneBlock(player, drawBlock, pixel[0], pixel[1], pixel[2], ref blocks, ref cannotUndo);
                if (err_1 > 0) {
                    pixel[1] += y_inc;
                    err_1 -= dx2;
                }
                if (err_2 > 0) {
                    pixel[2] += z_inc;
                    err_2 -= dx2;
                }
                err_1 += dy2;
                err_2 += dz2;
                pixel[0] += x_inc;
            }
        } else if ((m >= l) && (m >= n)) {
            err_1 = dx2 - m;
            err_2 = dz2 - m;
            for (i = 0; i < m; i++) {
                DrawOneBlock(player, drawBlock, pixel[0], pixel[1], pixel[2], ref blocks, ref cannotUndo);
                if (err_1 > 0) {
                    pixel[0] += x_inc;
                    err_1 -= dy2;
                }
                if (err_2 > 0) {
                    pixel[2] += z_inc;
                    err_2 -= dy2;
                }
                err_1 += dx2;
                err_2 += dz2;
                pixel[1] += y_inc;
            }
        } else {
            err_1 = dy2 - n;
            err_2 = dx2 - n;
            for (i = 0; i < n; i++) {
                DrawOneBlock(player, drawBlock, pixel[0], pixel[1], pixel[2], ref blocks, ref cannotUndo);
                if (err_1 > 0) {
                    pixel[1] += y_inc;
                    err_1 -= dz2;
                }
                if (err_2 > 0) {
                    pixel[0] += x_inc;
                    err_2 -= dz2;
                }
                err_1 += dy2;
                err_2 += dx2;
                pixel[2] += z_inc;
            }
        }

        // END LINE CODE
    }

I don't understand the code, but based on the direction Jonas put me in I was able to implement this and test without flaw.

0

精彩评论

暂无评论...
验证码 换一张
取 消