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

CadQuery中圆锥减切半球时的异常问题求助

问题描述

我需要从球壳(由半径10和8的球体差集得到)中减切4个分别沿(1,0,0)、(-1,0,0)、(0,1,0)、(0,-1,0)方向且相互交叉的圆锥。但交叉时其中一个部分无法被减切,非交叉圆锥能得到预期结果;替换为圆柱减切则一切正常。相关代码如下:

import math
import cadquery as cq
from ocp_vscode import show_object, reset_show, set_defaults

def align(obj, dir):
    l = math.sqrt(dir[0]**2+dir[1]**2+dir[2]**2); ## radial distance
    b = math.degrees(math.acos(dir[2]/l));        ## inclination angle
    c = math.degrees(math.atan2(dir[1],dir[0]));  ## azimuthal angle
    return obj.rotate((0,0,0),(1,0,0),0).rotate((0,0,0),(0,1,0),b).rotate((0,0,0),(0,0,1),c)

def cone(height, angle):
    radius = height*math.tan(math.radians(angle))
    cone = cq.Solid.makeCone(0, radius, height)
    return cone

def cylinder(height, angle):
    radius = height*math.tan(math.radians(angle))
    cylinder = cq.Solid.makeCylinder(radius, height)
    return cylinder

# BUILD
# ----------------------------------------------------------------
reset_show()
angle = 30

def piece(listCone):
    ## construct general parts
    shellOuter  = cq.Solid.makeSphere(10).cut(cq.Solid.makeSphere(8))
    pieceOuter = shellOuter
    ## generate the piece
    for element in listCone:
        ## outer shell
        obj = cone(10.01,angle)
        obj = align(obj, element)
        pieceOuter = pieceOuter.cut(obj)
    return pieceOuter

center = piece([
    (0,1,0),
    (0,-1,0),
    (1,0,0),
    (-1,0,0)
    ])
show_object(center, options=dict(alpha=0.5,color='red'))
问题原因

核心是圆锥顶点重合导致的布尔运算精度问题:

  • 默认创建的圆锥顶点在原点,旋转后四个圆锥的顶点都落在球壳中心位置。
  • OpenCASCADE(CadQuery依赖的几何内核)处理顶点完全重合的布尔减切时,容易出现边界判定错误,导致其中一个圆锥的减切失效。
  • 圆柱无此问题是因为它没有尖锐顶点,布尔运算的边界判定更稳定。
解决方案

通过让圆锥顶点避开球壳中心,就能解决这个问题,有两种简单实现方式:

方式一:平移圆锥避开中心

在piece函数的循环中,给旋转后的圆锥加一段沿自身方向的微小平移:

obj = align(obj, element).translate(element)  # 沿圆锥方向平移单位向量距离,让顶点离开中心

方式二:修改圆锥生成逻辑

调整cone函数,让圆锥顶点初始就偏移一段微小距离:

def cone(height, angle):
    radius = height*math.tan(math.radians(angle))
    # 让圆锥顶点沿Z轴偏移0.01,避免在原点
    cone = cq.Solid.makeCone(0, radius, height).translate((0, 0, 0.01))
    return cone

另外可以优化align函数,用更高效的向量旋转方式替代多次旋转:

def align(obj, dir):
    dir_vec = cq.Vector(dir).normalized()
    z_axis = cq.Vector(0,0,1)
    if dir_vec.isParallelTo(z_axis):
        return obj
    rotation_axis = z_axis.cross(dir_vec)
    rotation_angle = math.degrees(z_axis.getAngle(dir_vec))
    return obj.rotate((0,0,0), rotation_axis, rotation_angle)

完整修改后的代码

import math
import cadquery as cq
from ocp_vscode import show_object, reset_show, set_defaults

def align(obj, dir):
    dir_vec = cq.Vector(dir).normalized()
    z_axis = cq.Vector(0,0,1)
    if dir_vec.isParallelTo(z_axis):
        return obj
    rotation_axis = z_axis.cross(dir_vec)
    rotation_angle = math.degrees(z_axis.getAngle(dir_vec))
    return obj.rotate((0,0,0), rotation_axis, rotation_angle)

def cone(height, angle):
    radius = height*math.tan(math.radians(angle))
    cone = cq.Solid.makeCone(0, radius, height)
    return cone

def cylinder(height, angle):
    radius = height*math.tan(math.radians(angle))
    cylinder = cq.Solid.makeCylinder(radius, height)
    return cylinder

# BUILD
# ----------------------------------------------------------------
reset_show()
angle = 30

def piece(listCone):
    shellOuter  = cq.Solid.makeSphere(10).cut(cq.Solid.makeSphere(8))
    pieceOuter = shellOuter
    for element in listCone:
        obj = cone(10.01,angle)
        obj = align(obj, element).translate(element)
        pieceOuter = pieceOuter.cut(obj)
    return pieceOuter

center = piece([
    (0,1,0),
    (0,-1,0),
    (1,0,0),
    (-1,0,0)
    ])
show_object(center, options=dict(alpha=0.5,color='red'))

内容的提问来源于stack exchange,提问作者Pa Dalton

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 10:45:27