体三维显示器-Matlab的三维切片

少女dtysky

世界Create

时刻2015.03.22


体三维显示器为真三维显示的一种,即可以在真是的空间内显示一个三维的图像,图像占据一个真实的三维空间,不需要借助任何辅助视觉器件,我采用的方案是二维LED点阵的旋转,LED点阵为自行设计,这里记录了三维切片方面的东西。
老实说这只是一个无奈的方案,因为各种各样的原因吧,比如时间和优先级之类的,现在只能切规则几何体,并且要手动输入坐标对。
同时由于没有使用矩阵运算,所以切片会很慢。。。嘛,暂时就这样了,基本所有切片算法的思路都是这样的。


1.实现

分为定义端点、获取边界、构造边方程、构造切片平面并切片、平面坐标映射、坐标系变换、去归一化几个部分
1.端点定义:
这段代码定义了一个规则正方体的十二条边:

p=zeros(12,2,3);
p(1,:,:)=[[-1.6079,-0.4078,-0.4982];[-0.4690, 1.0140,-1.3235]];
p(2,:,:)=[[-0.4690, 1.0140,-1.3235];[ 1.0920,-0.2363,-1.3235]];
p(3,:,:)=[[-0.0470,-1.6582,-0.4982];[ 1.0920,-0.2363,-1.3235]];
p(4,:,:)=[[-0.0470,-1.6582,-0.4982];[-1.6079,-0.4078,-0.4982]];
p(5,:,:)=[[-1.6079,-0.4078,-0.4982];[-1.0920, 0.2363, 1.3235]];
p(6,:,:)=[[ 0.0470, 1.6582, 0.4982];[-0.4690, 1.0140,-1.3235]];
p(7,:,:)=[[ 1.0920,-0.2363,-1.3235];[ 1.6079, 0.4078, 0.4982]];
p(8,:,:)=[[ 0.4690,-1.0140, 1.3235];[-0.0470,-1.6582,-0.4982]];
p(9,:,:)=[[-1.0920, 0.2363, 1.3235];[ 0.0470, 1.6582, 0.4982]];
p(10,:,:)=[[ 1.6079, 0.4078, 0.4982];[ 0.0470, 1.6582, 0.4982]];
p(11,:,:)=[[ 0.4690,-1.0140, 1.3235];[ 1.6079, 0.4078, 0.4982]];
p(12,:,:)=[[-1.0920, 0.2363, 1.3235];[ 0.4690,-1.0140, 1.3235]];


2.获取边界:
确定整个立方体的边界范围,以此作为方程解的定义域:

tmpx=p(:,1,1);
tmpy=p(:,1,2);
tmpz=p(:,1,3);
tmpx2=p(:,2,1);
tmpy2=p(:,2,2);
tmpz2=p(:,2,3);
xmax=max([max(tmpx),max(tmpx2)]);
xmin=min([min(tmpx),min(tmpx2)]);
ymax=max([max(tmpx),max(tmpx2)]);
ymin=min([min(tmpy),min(tmpy2)]);
zmax=max([max(tmpz),max(tmpz2)]);
zmin=min([min(tmpz),min(tmpz2)]);


3.构造边的方程组:
由于任意一条空间直线的方程都可以表现为:

a11*X + a12*y + a13*0 = a14
a21*x + a22*0 + a23*z = a24

这样的形式的方程组,所以对于每一个边,需要算6个系数:

a=zeros(length(p(:,1,1)),3,4);
for i = 1:length(p(:,1,1))
    x1=p(i,1,1);
    x2=p(i,2,1);
    y1=p(i,1,2);
    y2=p(i,2,2);
    z1=p(i,1,3);
    z2=p(i,2,3);
    %x,y
    a(i,1,1)=y2-y1;
    a(i,1,2)=-(x2-x1);
    a(i,1,3)=0;
    a(i,1,4)=(x2-x1)*y1-(y2-y1)*x1;
    %y,z
    a(i,2,1)=0;
    a(i,2,2)=-(z2-z1);
    a(i,2,3)=y2-y1;
    a(i,2,4)=(z2-z1)*y1-(y2-y1)*z1;
end


4.构造切面并切片:
由于本设备是按绕中轴旋转,所以构造的切面应当符合“每旋转i度”构造一次的规则,本次设置为没旋转一度切一次,总共360切片,由于旋转过程中出现的对称现象,所以实际上只需要切一半(180度)即可:

res=[];
ii=1;
for i = 1:180
    b=[tan(deg2rad(i)),-1,0,0];
    for j = 1:length(a(:,1,1))
        f1=strcat( num2str(a(j,1,1)),'*x+',num2str(a(j,1,2)),'*y+',num2str(a(j,1,3)),'*z+',num2str(a(j,1,4)));
        f2=strcat( num2str(a(j,2,1)),'*x+',num2str(a(j,2,2)),'*y+',num2str(a(j,2,3)),'*z+',num2str(a(j,2,4)));
        f3=strcat( num2str(a(j,3,1)),'*x+',num2str(a(j,3,2)),'*y+',num2str(a(j,3,3)),'*z+',num2str(a(j,3,4)));
        f4=strcat( num2str(b(1)),'*x+',num2str(b(2)),'*y+',num2str(b(3)),'*z+',num2str(b(4)));
        [x,y,z]=solve(f1,f2,f4,'x','y','z');
        if ~isempty(x) && ~isempty(y) && ~isempty(z)
            if (x>=xmin && x<=xmax) && (y>=ymin && y<=ymax) && (z>=zmin && z<=zmax)
                res(ii,:)=[x,y,z];
                ii=ii+1;
            end
        end 
    end
    res(ii,:)=[0 0 0];
    ii=ii+1;
end


5.平面坐标映射:
这个比较容易理解,由于切得的坐标是空间坐标,而在LED显示的是平面坐标,所以要进行一下坐标映射,基本思路就是保留空间矢量的Z分量,将其作为新平面矢量的Y分量,而后取空间点相对于中轴的距离作为新的X分量即可:

tymax=max(abs(zmax),abs(zmin));
res2=[];
for i = 1:length(res)
    if res(i,1)==0 && res(i,2)==0 && res(i,3)==0
        res2(i,:)=[0 0];
    else
        x=res(i,1);
        y=res(i,2);
        z=res(i,3);
        tx=sqrt(x^2+y^2);
        if y<0
            res2(i,1)=-tx;
        else
            res2(i,1)=tx;
        end
        ty=z;
        res2(i,2)=zmax-ty;
    end
end

注意,这里同时进行了Y轴坐标系的变换。
之后进行对称操作,便可以得到另一半的切片:

len=length(res2);
for i=1:length(res2)
    res2(len+i,1)=-res2(i,1);
    res2(len+i,2)=res2(i,2);
end


6.坐标系变换和去归一化:
这个没什么好说的,根据自己实际的情况选取参数,对切片结果进行X向的坐标系变换和方大取整:

resfin=[];
for i =1:length(res2)
    resfin(i,:)=round(res2(i,:)*36);
    if resfin(i,1)==0 && resfin(i,2)==0
    else
       resfin(i,1)=resfin(i,1)+59;
    end
end

2.结果

以下是上面例子的输出,由于上面所言的边界问题有些瑕疵:
图解

完整代码看这里:

https://github.com/dtysky/3D_Displayer_Controller/blob/master/VHDL_PLANB/DECODER/CreatMif/test.m

如果不是自己的创作,少女是会标识出来的,所以要告诉别人是少女写的哦。