3D打印技术之切片引擎(5)

【此系列文章基于熔融沉积( fused depostion modeling, FDM )成形工艺】

从这一篇文章开始,就开始说填充。在3D打印切片技术中,填充算法是最核心的部分。3D打印技术的常用的指标包括:打印速度,稳固性,柔韧性,逼真度,密度(关系着使用打印材料的多少),都与填充算法有着直接的关系。可以说衡量一个切片引擎的优劣主要看它的填充算法是否优越。

其实我开始研发切片引擎的时候,由于不是所有的模型打印都需要填充,所以前面的算法开发时间很短,大部分时间耗在了填充算法的研究上,也就是填充算法的不断优化使得我的引擎的打印质量不断的接近于skeinforge。

填充算法是一个世界难题,目前世界上最先进的填充算法也还是问题多多,如何最优化的填充使得模型兼顾稳固逼真,而且密度小,打印速度快。这不是简单的二维几何填充所能做的到的。我大学的专业是临床医学,记得人体解剖学中提到过的骨小梁结构,其实才是最合理的填充方式,但是如何用数学实现骨小梁,任重而道远啊。

行,马上进入正题,在这一篇文章里,我就先说一下填充算法中最简单的一种——线填充。

首先,需要引用计算机图形学二维图形的扫描线填充算法。如下图:

简单的描述:射线与多边形相交,与边界交点个数为奇数,为内点;偶数为外点或者边界点。但是,还有几种特殊情况需要特殊处理:每当一条扫描线经过多边形的一个顶点时,扫描线在该顶点处与多边形的两条边相交,这种情况可能导致在这条扫描线的交点列表上要增加两个点。当扫描线与奇数边相交时,这时候需要把扫描线与两侧的有公共交点的线的交点算作一个交点。如下图b线:

另外,还有边界线与扫描线重合的情况,需要完全规避掉。

对于最简单的线填充,掌握这个原理基本就没有问题了,所需要注意的就是上下两层不能同一方向的填充,避免打印的模型不稳定,可以考虑采用依次采用x轴平行的方向和y轴平行的方向。这样用一组等间距的平行线扫描,采用上述的算法,最简单的线填充就完成了。

但这样的线填充会引出一个问题,回顾一下在第一讲中说到的——一个成功的切片引擎应该满足的几点,这样的填充虽然看似基本满足要求,但绝大多数情况在这几点上做的很不够,尤其是某一层是个狭长的结构,每一行的扫描线就非常的短,打印机就走走停停,不断的更换打印方向,打印速度受到极大的影响,更重要的是扫描线对边界冲击次数太多,肯定要影响到表面的效果,影响美观,而且频繁的走走停停,更换方向,对机器的寿命也是极大的消耗。

可以考虑这样的解决方案,第一步:把某一层的二维图形分割成若干个凸集或者近似凸集。第二步,对每一个凸集进行主元分析。第三步,利用得到的主元方向,先把x轴或者y轴变换到与主元平行的方向,这里假设该线性变换为M,利用上述的算法进行填充,然后再利用M?1变换回原来的位置。

主元分析是利用协方差矩阵确定边界向量的主元向量,这里的点是二维,可简单表示:

μx=12n∑ni=1(beginPointxi+endPointxi) (1)

其中beginPointxi为第i个边界矢量的起始向量的x分量,endPointxi为第i个边界矢量的结束向量的x分量;y分量以此类推。下面就开始构造协方差矩阵C,C的第j行第k列元素如下(1?j,k?2):

Ckj=12n∑ni=1(beginPointjibeginPointki+endPointjiendPointki) (2)

其中:beginPoint1i=beginPointxi?μx,其他的以此类推。

最后根据数值计算求出协方差矩阵的三个特征向量,组成特征矩阵。特征矩阵即为我们要找的线性变换M。这是由于n维线性空间V到线性空间V‘的线性映射完全被它在V的一个基上的作用所决定。这里V=V‘,原空间的基可认定为:(1,0),(0,1)。则:

(10 01)×M=(ε1ε2)

其中ε1和ε2为矩阵C的两个特征向量。乘号左边是单位矩阵,所以M即等于右边。下面给出主元分析的代码。


/// <summary>
/// 向量减去平均向量
/// </summary>
/// <param name="v1">向量端点1</param>
/// <param name="v2">向量端点2</param>
/// <param name="pha">边界向量</param>
/// <param name="coor_sum">向量平均值</param>
void get_phasor_covariance( float3& v1, float3& v2, Phasor* pha, float3 coor_sum )
{
    v1[0] = pha->beginPoint[0] - coor_sum[0];

    v1[1] = pha->beginPoint[1] - coor_sum[1];

    v2[0] = pha->endPoint[0] - coor_sum[0];

    v2[1] = pha->endPoint[1] - coor_sum[1];
}

/// <summary>
/// 得到协方差矩阵元素
/// </summary>
/// <param name="pha">边界向量集合</param>
/// <param name="pha_num">边界向量数量</param>
/// <param name="coor_sum">平均向量</param>
/// <param name="j">矩阵行索引</param>
/// <param name="k">矩阵列索引</param>
float get_covariance_matrix_element( Phasor* pha, int pha_num, float3 coor_sum, int j, int k )
{
    float element = 0;

    float3 v1, v2;

    for ( int i = 0; i != pha_num; ++i )
    {
        get_phasor_covariance( v1, v2, pha + i, coor_sum );
        element += v1[j] * v1[k] + v2[j] * v2[k];
    }

    element /= pha_num * 2;

    return element;
}

/// <summary>
/// 获得平均向量
/// </summary>
/// <param name="coor_sum">平均向量</param>
/// <param name="ps">边界环</param>
/// <param name="pha_num">边界环向量的数量</param>
void get_coor2_aver( float3& coor_sum, Phasor* ps, int pha_num )
{
    int i;

    memset(coor_sum,0,sizeof(float)*3);

    for ( i = 0; i != pha_num; ++i )
    {
        coor_sum[0] += ps[i].beginPoint[0];

        coor_sum[0] += ps[i].endPoint[0];

        coor_sum[1] += ps[i].beginPoint[1];

        coor_sum[1] += ps[i].endPoint[1];
    }

    coor_sum[0] /= pha_num * 2;
    coor_sum[1] /= pha_num * 2;
}

/// <summary>
/// 从协方差矩阵得到特征矩阵
/// </summary>
/// <param name="a">协方差矩阵</param>
/// <param name="n">矩阵阶数</param>
/// <param name="v">特征矩阵</param>
/// <param name="eps">控制精度要求</param>
/// <param name="jt">控制最大迭代次数</param>
int eejcb( float a[], int n, float v[], float eps, int jt )
{
    int i, j, p, q, u, w, t, s, l;
    float fm, cn, sn, omega, x, y, d;
    l = 1;
    for ( i = 0; i <= n - 1; i++ )
    {
        v[i * n + i] = 1.0;
        for ( j = 0; j <= n - 1; j++ )
        {
            if ( i != j )
            {
                v[i * n + j] = 0.0;
            }
        }
    }
    while ( 1 == 1 )
    {
        fm = 0.0;
        for ( i = 0; i <= n - 1; i++ )
        {
            for ( j = 0; j <= n - 1; j++ )
            {
                d = fabs( a[i * n + j] );
                if ( ( i != j ) && ( d > fm ) )
                {
                    fm = d;
                    p = i;
                    q = j;
                }
            }
        }
        if ( fm < eps )
        {
            return( 1 );
        }
        if ( l > jt )
        {
            return( -1 );
        }
        l = l + 1;
        u = p * n + q;
        w = p * n + p;
        t = q * n + p;
        s = q * n + q;
        x = -a[u];
        y = ( a[s] - a[w] ) / 2.0;
        omega = x / sqrt( x * x + y * y );
        if ( y < 0.0 )
        {
            omega = -omega;
        }
        sn = 1.0 + sqrt( 1.0 - omega * omega );
        sn = omega / sqrt( 2.0 * sn );
        cn = sqrt( 1.0 - sn * sn );
        fm = a[w];
        a[w] = fm * cn * cn + a[s] * sn * sn + a[u] * omega;
        a[s] = fm * sn * sn + a[s] * cn * cn - a[u] * omega;
        a[u] = 0.0;
        a[t] = 0.0;
        for ( j = 0; j <= n - 1; j++ )
        {
            if ( ( j != p ) && ( j != q ) )
            {
                u = p * n + j;
                w = q * n + j;
                fm = a[u];
                a[u] = fm * cn + a[w] * sn;
                a[w] = -fm * sn + a[w] * cn;
            }
        }
        for ( i = 0; i <= n - 1; i++ )
        {
            if ( ( i != p ) && ( i != q ) )
            {
                u = i * n + p;
                w = i * n + q;
                fm = a[u];
                a[u] = fm * cn + a[w] * sn;
                a[w] = -fm * sn + a[w] * cn;
            }
        }
        for ( i = 0; i <= n - 1; i++ )
        {
            u = i * n + p;
            w = i * n + q;
            fm = v[u];
            v[u] = fm * cn + v[w] * sn;
            v[w] = -fm * sn + v[w] * cn;
        }
    }
    return( 1 );
}

/// <summary>
/// 得到协方差矩阵
/// </summary>
/// <param name="cm">矩阵</param>
/// <param name="pha">边界向量集合</param>
/// <param name="pha_num">边界向量数量</param>
void get_covariance_matrix( float cm[], Phasor* pha, int pha_num )
{
    float3 coor_sum;

    get_coor2_aver( coor_sum, pha, pha_num );

    cm[0] = get_covariance_matrix_element( pha, pha_num, coor_sum, 0, 0 );
    cm[1] = get_covariance_matrix_element( pha, pha_num, coor_sum, 1, 0 );

    cm[2] = get_covariance_matrix_element( pha, pha_num, coor_sum, 0, 1 );
    cm[3] = get_covariance_matrix_element( pha, pha_num, coor_sum, 1, 1 );
}

/// <summary>
/// 从边界向量集合得到特征矩阵
/// </summary>
/// <param name="matrix_changeOfBase">特征矩阵</param>
/// <param name="pha">边界向量集合</param>
/// <param name="pha_num">边界向量数量</param>
int get_matrix_changeOfBase( float matrix_changeOfBase[], Phasor* pha, int pha_num )
{
    float cm[4] =
    {
        0
    };

    get_covariance_matrix( cm, pha, pha_num );

    return eejcb( cm, 2, matrix_changeOfBase, 0.0000001, 600 );
}

接近尾声要添加新的填充矢量把上边生成的填充矢量依次的首尾连接起来,要重新排序填充矢量,这样保证了打印机最小的断开次数和时间。这里要注意:选择最佳起始填充矢量,要把离得最近的填充矢量首尾相接;要保证全局最佳,而非局部最佳;避免扫描线频繁的跳转,最理想的情况是让扫描线“一笔画下来”。还要注意:扫描线太短机器疾走急停,如果扫描线太短,可以忽略掉。连接完毕的结果如下图(蓝绿相间的为填充线):

这篇文章就先到这,下面几篇文章将介绍另外几种填充算法。

参考文献:

[1] 计算机图形学 Donald Hearn M.Pauline Baker著 蔡士杰等译

[2] 高等代数 丘维声著

转载请注明出处:http://blog.csdn.net/fourierFeng/article/details/47099289

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-29 19:05:50

3D打印技术之切片引擎(5)的相关文章

3D打印技术之切片引擎(3)

[此系列文章基于熔融沉积( fused depostion modeling, FDM )成形工艺] 从这一篇文章开始,我讲一下实体切片方面的一些技术. 切片引擎,实体部分大致包括: 提取边界矢量-->添加多边-->生成填充矢量集合. 其中最难的是生成填充矢量集合,也是整个切片引擎技术的核心,因为衡量一款引擎的好坏的四个核心要素--稳固性:与原始模型的相似度:用了多少材料:打印快慢,都是主要取决于填充矢量的生成技术.目前生成填充矢量的算法还有很多未知的问题有待攻克,对于这一块技术我将在最后与大

3D打印技术之切片引擎(1)

切片引擎,是3D打印技术的灵魂,其实质就是将3D模型进行分层,输出矢量集合,是应该归为图形学的一门技术,其对数学算法要求很高,复杂性主要体现在要对3D模型的不同的拓扑结构做出判断并输出与之相符合的矢量集合.就我个人的观点,目前世界上最优秀的切片引擎在这一点上也没有做的很好,所以对于这一门技术,可进步的空间是很大的,未知的空间非常辽阔. 非常有幸的是在我的上一家公司里面,我有机会开发这样的一个模块,我负责由3D模型生成矢量集合,另一位同事把我生成的矢量集合再生成为Gcode,据我所知,目前国内还没

3D打印技术之切片引擎(2)

[此系列文章基于熔融沉积( fused depostion modeling, FDM )成形工艺] 在上一篇文章中,我从总体上介绍了一下切片引擎技术,从这一篇文章开始,我开始说一下具体的技术细节. 切片引擎,实体部分包括: 提取边界向量-->添加多边-->生成填充向量集合. 还包括生成支撑向量集合.就先说下支撑算法. 生成支撑向量集合分为人工生成和自动生成两种,优越的支撑算法主要体现在 1,省材料. 2,生成速度快. 3,生成的支撑体与零件实体较少的接触与干涉,有利于剥离. 毕竟,切片引擎的

3D打印技术之切片引擎(4)

[此系列文章基于熔融沉积( fused depostion modeling, FDM )成形工艺] 这一篇文章我讲一下多边打印的问题,多边打印是切片引擎的一项关键的技术,首先,它可以保证打印实体表面免受内部填充的冲击,保证外观的真实度:其次,在上层在相对于下层倾斜较大时,多边打印可以很好的起到支撑的作用,避免上层塌陷. 但是,目前来说,我的多边打印还不够普适,对一些不规范的模型,以及模型中非常尖锐的特征效果并不好,对绝大部分的较为平滑的模型是完全没有问题的. 下面就简单说一下它的原理:假设边界

重庆用3D打印技术修复世界最大千手观音

近日,重庆市大足石刻景区在千手观音主尊修复工程中首次引入3D打印技术.记者在修复现场看到,缩小版的千手观音主尊像的3D打印模型比例为1:3,打印模型主要用于主尊像的形态修复.? 重庆市大足县宝顶山千手观音雕刻于南宋中后期,距今有800多年历史.整个石刻佛像,包括一个千手观音主尊和四个侍者,有7.7米高.12.5米宽.刻在15?30米高的悬崖上,覆盖了近88平米的崖面,是世界上最大的千手观音雕像.精密拋光? 这次针对千手观音的大型修复工作,是由政府拨款进行的.研究人员没有采用传统的模具和铸造的方法

LAIKA公司应用ProJet 660 色彩3D打印技术改变3D动漫脸部定格动画

LAIKA公司应用 ProJet 660 色彩3D打印技术改变3D动漫脸部定格动画 通过使用彩色3D打印机,我们不仅能够推动面部表情效能提升到一个新的水平,同时我们也能实现人物的脸部的细节和微妙变化水平.布赖恩?麦克莱恩 - 如果你曾经想知道建立3D动画制作需要进行多少工作,得到的答案是"很多,很多" - 特别是如果工作依靠被称为停止运动的技术.定格动画需要动画师捕捉每个操纵建模字符拼凑一个流畅自然的动作.例如只是一秒的面部表情动作,可能12到24种不同的模型改变是必需的. 实现一站式

苹果公司欲使用3D打印技术进行新产品的测试

苹果公司的产品是很多人都在追随的,作为世界上最大的电子产品的企业来说,因为它的产品给了人们更好的用户体验.怎么才能做到这一点呢,这就源于他们的输入设计实验室,这里可以称之为最隐秘并且先进的原型测试实验室其中的一个.这里有很多非常精密的仪器,也有很多为了完美产品的工作人员,不管是从外观体验感以及性能他们都希望能够带给大家完美的产品. 在最近一次苹果公司的公关活动中,他们向媒体展示了这个实验室,让它从神秘不为人知的背后走到了人们的面前,一方面表现出他们对于产品性能测试的重视,同时也向人们说明他们对于

3D打印技术让实现更多天马行空的创意

在3D打印技术还没有出现的时候,我们脑海中一些天马行空的想象是没办法实现的.也有很多创意相关的工作人员在叹息,似乎有一种生不逢时的遗憾.近年来,随着3D打印技术越来越成熟,自由设计成为一种优势弥补了一些在传统制造业的不足,似乎个性化的定制才是一种时尚和潮流的标志.在这样的背景下,一大批充满着个性化的创意产品走入人们的生活,让人们对创意有了更新更奇特的了解. 不久前在微博上热传的一组雕塑图片被很多人转发.一些具有重庆本土特色的方言被创意者形象的刻在了雕塑上,所以当人们看到最终的作品时很有感触.这组

3D打印技术的研究成果-3D打印机厂家

3D打印技术,是以计算机三维设计模型为蓝本,通过软件分层离散和数控成型系统,利用激光束.热熔喷嘴等方式将金属粉末.陶瓷粉末.塑料.细胞组织等特殊材料进行逐层堆积粘结,最终叠加成型,制造出实体产品.与传统制造业通过摸具.车铣等机械加工方式对原材料进行定型.切削以最终生产成品不同,3D打印将三维实体变为若干个二维平面,通过对材料处理并逐层叠加进行生产.不需要众多的人力,直接从计算机图形数据中便可生成任何形状的零件,使生产制造得以向更广的生产人群范围延伸. 然而,通过专项研究,3D打印主要的技术形式有