You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

OpenSCAD polyhedron差集运算失败:网格未闭合报错求助

OpenSCAD Polyhedron差集运算报错:ERROR: The given mesh is not closed! Unable to convert to CGAL_Nef_Polyhedron

问题现象

  • F5预览完全正常,但F6渲染差集时失败,报错提示网格未闭合
  • 单独渲染polyhedron模型(不做差集)时,F5和F6都正常,甚至能导出STL
  • 已尝试用F12检查面的手性,调整点顺序使所有面手性一致,但问题依旧
  • 使用hull()包裹形状后差集可正常工作,但hull()无法处理复杂凹形,不符合需求

复现代码

//提示:F12可按面的手性着色,绿色为顺时针,紫色为逆时针,所有面的点遍历方向需一致

y_separation = 10.0;

module poly_prism_segmented()
{
    //分段数量
    cn_segments = 2;
 
    cx_length = 30.0;
    cy_width = y_separation;
    cz_thickness = 3.0;
    
    //      索引规则
    //  底部外侧     [0N, 1N-1]
    //  底部内侧     [1N, 2N-1]
    //  顶部内侧     [2N, 3N-1]
    //  顶部外侧     [3N, 4N-1]
    
    //将棱柱拆分为四条线
    ap_bottom_outer =
    [
        for (cnt_n = [0:cn_segments-1])
            let (k=cnt_n/(cn_segments-1),x_temp = cx_length*k)
            [x_temp,+cy_width/2,0]

    ];

    ap_bottom_inner =
    [
        for (cnt_n = [0:cn_segments-1])
            let (k=cnt_n/(cn_segments-1),x_temp = cx_length*k)
            [x_temp,-cy_width/2,0]

    ];

    ap_top_inner =
    [
        for (cnt_n = [0:cn_segments-1])
            let (k=cnt_n/(cn_segments-1),x_temp = cx_length*k)
            [x_temp,-cy_width/2,cz_thickness]

    ];

    ap_top_outer =
    [
        //遍历每个分段
        for (cnt_n = [0:cn_segments-1])
            let (k=cnt_n/(cn_segments-1),x_temp = cx_length*k)
            [x_temp,+cy_width/2,cz_thickness]

    ];
    
    //拼接所有点,顺序很重要
    ap_points =
    concat
    (
        ap_bottom_outer,
        ap_bottom_inner,
        ap_top_inner,
        ap_top_outer
    );
        
    //首尾端面:第一个面取各线的第一个点,最后一个面取各线的最后一个点
    ap_lid_endpoints = 
    [
        [0, cn_segments, cn_segments*2, cn_segments*3],
        [cn_segments-1, cn_segments*2-1, cn_segments*3-1, cn_segments*4-1]
    ]; 
    
    //      面的点顺序规则
    //  顶部     | 顶部内侧        顶部外侧     下一个顶部外侧        下一个顶部内侧
    //  内侧     | 底部内侧        顶部内侧     下一个顶部内侧        下一个底部内侧
    //  底部     | 底部内侧        底部外侧     下一个底部外侧        下一个底部内侧
    //  外侧     | 底部外侧        顶部外侧     下一个顶部外侧        下一个底部外侧
    
    //顶部面
    //  顶部内侧      [2N, 3N-1]
    //  顶部外侧      [3N, 4N-1]
    ap_box_top = 
    [
        //遍历每个分段
        for (cnt_n = [0:cn_segments-2])  
            //创建四边形面,包含四个点的索引
            [
                //顶部内侧             [2N, 3N-2] 
                cn_segments*2 +cnt_n,
                //顶部外侧             [3N, 4N-2]
                cn_segments*3 + cnt_n,
                //下一个顶部外侧        [3N+1, 4N-1]
                cn_segments*3 + cnt_n + 1,
                //下一个顶部内侧        [2N+1, 3N-1] 
                cn_segments*2 + cnt_n + 1
            ],
    ];    
        
    //底部面
    //  底部外侧     [0N, 1N-1]
    //  底部内侧     [1N, 2N-1]
    ap_box_bot = 
    [
        //遍历每个分段
        for (cnt_n = [0:cn_segments-2])  
            //创建四边形面,包含四个点的索引
            [
                //底部外侧             [0N, 1N-2] 
                cn_segments*0 +cnt_n,
                //底部内侧             [1N, 2N-2]
                cn_segments*1 + cnt_n,
                //下一个底部内侧        [1N+1, 2N-1]
                cn_segments*1 + cnt_n + 1,
                //下一个底部外侧        [0N+1, 1N-1] 
                cn_segments*0 + cnt_n + 1
            ],
    ];    
        
    //内侧面
    //  底部内侧     [1N, 2N-1]
    //  顶部内侧     [2N, 3N-1]
    //如果按顶部、底部、底部、顶部的顺序,会导致旋转方向错误,进而出现开放网格
    ap_box_in = 
    [
        //遍历每个分段
        for (cnt_n = [0:cn_segments-2])  
            //创建四边形面,包含四个点的索引
            [
                
                //底部内侧             [1N, 2N-2]
                cn_segments*1 + cnt_n,
                //顶部内侧             [2N, 3N-2] 
                cn_segments*2 +cnt_n,
                //下一个顶部内侧        [2N+1, 3N-1]  
                cn_segments*2 + cnt_n + 1,
                //下一个底部内侧        [1N+1, 2N-1]
                cn_segments*1 + cnt_n + 1
                
            ],
    ]; 

        
    //外侧面
    //  底部外侧     [0N, 1N-1]
    //  顶部外侧     [3N, 4N-1]
    ap_box_out = 
    [
        //遍历每个分段
        for (cnt_n = [0:cn_segments-2])  
            //创建四边形面,包含四个点的索引
            [
                //顶部外侧             [3N, 4N-2]
                cn_segments*3 + cnt_n,
                //底部外侧             [0N, 1N-2] 
                cn_segments*0 +cnt_n,
                //下一个底部外侧        [0N+1, 1N-1]  
                cn_segments*0 + cnt_n + 1,
                //下一个顶部外侧        [3N+1, 4N-1]
                cn_segments*3 + cnt_n + 1
            ],
    ];     


    //拼接所有面,传入polyhedron
    ap_faces =
    concat
    (
        ap_lid_endpoints,
        ap_box_top,
        ap_box_bot,
        ap_box_in,
        ap_box_out
    );   
        
    polyhedron
    (
        points = ap_points,
        faces = ap_faces
    );
}

translate([0,-y_separation,0])
poly_prism_segmented();

translate([0,y_separation,0])
difference()
{
    sphere(r=10,$fa=1,$fs=1);
    poly_prism_segmented();
}

问题分析与解决方案

核心原因

OpenSCAD的CGAL后端要求布尔运算的网格必须完全闭合且流形。F5预览用OpenGL渲染,容错性高;但F6的CGAL运算会严格检查网格拓扑,微小错误都会导致失败。你的代码存在两处关键问题:

  1. 首尾端面的手性错误:端面点顺序朝向模型内部,破坏了网格闭合性
  2. 外侧面的点顺序错误:遍历方向与其他面手性不一致,导致面朝向异常

修正代码

修正首尾端面

ap_lid_endpoints = 
[
    // 起始端面:调整为逆时针顺序(从模型左侧看),统一手性
    [0, cn_segments*3, cn_segments*2, cn_segments],
    // 结束端面:调整为逆时针顺序(从模型右侧看),统一手性
    [cn_segments-1, cn_segments, cn_segments*2, cn_segments*3-1]
]; 

修正外侧面

ap_box_out = 
[
    for (cnt_n = [0:cn_segments-2])  
        [
            // 调整点顺序,保持与其他面一致的手性
            cn_segments*0 +cnt_n,
            cn_segments*3 + cnt_n,
            cn_segments*3 + cnt_n + 1,
            cn_segments*0 + cnt_n + 1
        ],
];     

验证步骤

  1. 修正后按F12预览,所有面应显示为同一种颜色,说明手性统一
  2. 执行F6渲染差集,CGAL可成功转换网格,布尔运算正常

内容的提问来源于stack exchange,提问作者05032 Mendicant Bias

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 14:42:04