"绘图"一个三维线进入一个数组 [英] "Draw" a 3d line into an array

查看:146
本文介绍了"绘图"一个三维线进入一个数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在寻找一个解决方案,使用python绘制3d线(填充圆柱体)为3d数组,就像

您需要:


  1. 端点为球



    请参阅上面的链接中的 add_sphere

  2. 对于我们需要 U,V 的基础矢量(垂直矢量至终点

    彼此和线本身)。通过这些,我们可以简单地使用任何 2D 圆像素采集并轻松将它们转换为 3D 体素位置。因此,如果 u,v 是以(0,0) 2D >然后:

      x = x0 + u * Ux + v * Vx 
    y = y0 + u * Uy + v * Vy
    z = z0 + u * Uz + v * Vz

    (x,y,z)对应于三维体素坐标,中心(x0,y0,z0)。有关更多信息,请参阅



    代表 128x128x128 音量如下所示:

      // init volume raytracer 
    vol.gl_init();
    vol.beg();
    int r,a,b,c;
    r = 10.0;一个= R + 1; B = vol.size-R-2; C =&vol.size GT;大于1;
    // BBGGRR
    vol.add_line(a,a,a,b,a,a,r,0x00FF2020);
    vol.add_line(a,b,a,b,b,a,r,0x00FF2020);
    vol.add_line(a,a,a,a,b,a,r,0x00FF2020);
    vol.add_line(b,a,a,b,b,a,r,0x00FF2020);

    vol.add_line(a,a,b,b,a,b,r,0x00FF2020);
    vol.add_line(a,b,b,b,b,b,r,0x00FF2020);
    vol.add_line(a,a,b,a,b,b,r,0x00FF2020);
    vol.add_line(b,a,b,b,b,b,r,0x00FF2020);

    vol.add_line(a,a,a,a,b,r,0x00FF2020);
    vol.add_line(a,b,a,a,b,b,r,0x00FF2020);
    vol.add_line(b,a,a,b,a,b,r,0x00FF2020);
    vol.add_line(b,b,a,b,b,b,r,0x00FF2020);

    vol.add_sphere(c,c,c,c>> 1,0x00FF8040);
    vol.add_sphere(a,c,c,r,0x004080FF);
    vol.add_sphere(b,c,c,r,0x0080FF40);
    vol.add_sphere(c,a,c,r,0x00FF4080);
    vol.add_sphere(c,b,c,r,0x00AAAAAA);
    vol.add_box(c,c,a,r,r,r,0x0060FF60);
    vol.add_box(c,c,b,r,r,r,0x00FF2020);

    vol.end();


    I am looking for a solution do "draw" a 3d line (filled cylinder) into a 3d array using python just like the skimage.draw.line function does for 2 arrays.

    The line should have a starting point (x1, y1, z1), an end point (x2, y2, z2), and a radius R.

    I had a look at an example for a 2d line but I was not able to modify it to work in the 3d case.

    I was thinking about drawing successive ellipses into the array, but I was not able to figure out how to calculate the two axes and the rotation angle.

    Maybe there is a much simpler approach to this problem?

    解决方案

    Let assume this GLSL volumetric back raytracer as a start point. To make a filled 3D line like this:

    You need:

    1. endpoints as spheres

      see the add_sphere in the link above.

    2. discs cut at the endpoints

      for that we need U,V basis vectors (perpendicular vectors to each other and to line itself). With those we can simply use any 2D circle pixels acquisition and convert them to 3D voxel positions with ease. So if u,v are coordinates in some 2D circle centered at (0,0) then:

      x = x0 + u*U.x + v*V.x
      y = y0 + u*U.y + v*V.y
      z = z0 + u*U.z + v*V.z
      

      (x,y,z) are corresponding to 3D circle voxel coordinate with center (x0,y0,z0). For more info see my C++ glCircle3D implementation.

    3. line body

      As wee got all the voxel positions in the disc around x0,y0,z0 endpoint of the line just cast a line from each of it with the same slope as line (x0,y0,z0),(x1,y1,z1) which are the endpoints of your line.

    When put together in C++ (sorry I do not code in python) I got this:

    void volume::add_line(int x0,int y0,int z0,int x1,int y1,int z1,int r,GLuint col)
        {
        if (!_init) return;
        int i,n,x,y,z,cx,cy,cz,dx,dy,dz,kx,ky,kz;
        // endpoints are (half)spheres
        add_sphere(x0,y0,z0,r,col);
        add_sphere(x1,y1,z1,r,col);
        // DDA constants
        kx=0; dx=x1-x0; if (dx>0) kx=+1; if (dx<0) { kx=-1; dx=-dx; } dx++;           n=dx;
        ky=0; dy=y1-y0; if (dy>0) ky=+1; if (dy<0) { ky=-1; dy=-dy; } dy++; if (n<dy) n=dy;
        kz=0; dz=z1-z0; if (dz>0) kz=+1; if (dz<0) { kz=-1; dz=-dz; } dz++; if (n<dz) n=dz;
        // basis vectors
        double U[3],V[3],N[3]={x1-x0,y1-y0,z1-z0},u,v,rr=r*r;
        vector_one(N,N); // unit vector
        vector_ld(U,1.0,0.0,0.0); if (fabs(vector_mul(U,N))>=0.75) vector_ld(U,0.0,1.0,0.0); // |dot(U,N)|<0.75 means (1.0,0.0,0.0) is nearly parallel to N so chose (0.0,1.0,0.0) instead
        vector_mul(U,U,N); // U = U x N
        vector_mul(V,U,N); // V = U x N
        vector_one(U,U);   // U /= |U|
        vector_one(V,V);   // V /= |V|
        // disc
        for (u=-r;u<=+r;u++)
         for (v=-r;v<=+r;v++)
          if (u*u+v*v<=rr)
            {
            x=x0+double((u*U[0])+(v*V[0]));
            y=y0+double((u*U[1])+(v*V[1]));
            z=z0+double((u*U[2])+(v*V[2]));
            // DDA line
            for (cx=cy=cz=n,i=0;i<n;i++)
                {
                if ((x>=0)&&(x<size)&&(y>=0)&&(y<size)&&(z>=0)&&(z<size)) data[z][y][x]=col;
                cx-=dx; if (cx<=0) { cx+=n; x+=kx; }
                cy-=dy; if (cy<=0) { cy+=n; y+=ky; }
                cz-=dz; if (cz<=0) { cz+=n; z+=kz; }
                }
            }
        }
    

    The vector_xxx functions are just my 3D vector math and just dot,cross product and normalize to unit size are used which is easy to implement. You can see them here:

    There are still things that can be improved like the spheres could be just half spheres and their generation can be joined with the disc stuff ... as the dot between normal and not offseted 3D sphere coordinate is either positive/zero/negative which distinct endpoint half-sphere and disc ... that would also fully eliminate the need for U,V.

    Also depending on used HW and circumstances there might be also faster approaches like analytical (filling BBOX based on distance from line) if fast vector math is combined with massive parallelism like on GPU.

    After some tweaking in my engine (added zoom and handle some accuracy problem) I got this result:

    for 128x128x128 volume inited like this:

    // init volume raytracer
    vol.gl_init();
    vol.beg();
    int r,a,b,c;
    r=10.0; a=r+1; b=vol.size-r-2; c=vol.size>>1;
                                 //BBGGRR
    vol.add_line(a,a,a,b,a,a,r,0x00FF2020);
    vol.add_line(a,b,a,b,b,a,r,0x00FF2020);
    vol.add_line(a,a,a,a,b,a,r,0x00FF2020);
    vol.add_line(b,a,a,b,b,a,r,0x00FF2020);
    
    vol.add_line(a,a,b,b,a,b,r,0x00FF2020);
    vol.add_line(a,b,b,b,b,b,r,0x00FF2020);
    vol.add_line(a,a,b,a,b,b,r,0x00FF2020);
    vol.add_line(b,a,b,b,b,b,r,0x00FF2020);
    
    vol.add_line(a,a,a,a,a,b,r,0x00FF2020);
    vol.add_line(a,b,a,a,b,b,r,0x00FF2020);
    vol.add_line(b,a,a,b,a,b,r,0x00FF2020);
    vol.add_line(b,b,a,b,b,b,r,0x00FF2020);
    
    vol.add_sphere(c,c,c,c>>1,0x00FF8040);
    vol.add_sphere(a,c,c,r,0x004080FF);
    vol.add_sphere(b,c,c,r,0x0080FF40);
    vol.add_sphere(c,a,c,r,0x00FF4080);
    vol.add_sphere(c,b,c,r,0x00AAAAAA);
    vol.add_box(c,c,a,r,r,r,0x0060FF60);
    vol.add_box(c,c,b,r,r,r,0x00FF2020);
    
    vol.end();
    

    这篇关于&QUOT;绘图&QUOT;一个三维线进入一个数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆