likes
comments
collection
share

11.例子16-33 - CadQuery 中文文档

作者站长头像
站长
· 阅读数 4

16、复制工作平面

现有的 CQ 对象可以从另一个 CQ 对象复制工作平面 11.例子16-33 - CadQuery 中文文档

result = (
    cq.Workplane("front")
    .circle(1)
    .extrude(10)  # 做圆柱
    # 我们要制作与第一个圆柱垂直的第二个圆柱
    # 但我们没有工作面作为基础
    .copyWorkplane(
        # 创建一个包含所需工作平面的临时对象
        cq.Workplane("right", origin=(-5, 0, 0))
    )
    .circle(1)
    .extrude(10)
)

API References:Workplane.copyWorkplane() Workplane.circle() Workplane.extrude()Workplane()

17、旋转工作平面

可以创建一个相对于另一个工作平面旋转指定角度的工作平面。

11.例子16-33 - CadQuery 中文文档

result = (
    cq.Workplane("front")
    .box(4.0, 4.0, 0.25)
    .faces(">Z")
    .workplane()
    .transformed(offset=cq.Vector(0, -1.5, 1.0), rotate=cq.Vector(60, 0, 0))
    .rect(1.5, 1.5, forConstruction=True)
    .vertices()
    .hole(0.25)
)

使用 transformed 前后的工作平面变化 11.例子16-33 - CadQuery 中文文档

Api References:Workplane.transformed() Workplane.box() Workplane.rect()Workplane.faces()

18、使用构造几何

您可以绘制形状,将其顶点作为点来定位其他特征,但这个形状本身并不用于构造几何。 这种用于定位其他特征,而不创建几何的特征,称为构造几何!!#ff0000 Construction Geometry!!。

在下面的示例中,绘制了一个矩形,其顶点用于定位一组孔。

11.例子16-33 - CadQuery 中文文档

result = (
    cq.Workplane("front")
    .box(2, 2, 0.5)
    .faces(">Z")
    .workplane()
    .rect(1.5, 1.5, forConstruction=True)
    .vertices()
    .hole(0.125)
)

Api References: Workplane.rect() (forConstruction=True) Selectors Reference Workplane.workplane() Workplane.box()Workplane.hole()Workplane()

19、抽壳创建薄壁特征

抽壳是将实体对象转换为厚度均匀的壳。

使用形状的Workplane.shell()方法 ,传入一个负的厚度参数,就可以给对象抽壳并“挖空”内部。 11.例子16-33 - CadQuery 中文文档

result = cq.Workplane("front").box(2, 2, 2).shell(-0.1)

正的厚度参数用圆角外边缘包裹对象,原始对象将是“挖空”部分。

11.例子16-33 - CadQuery 中文文档

result = cq.Workplane("front").box(2, 2, 2).shell(0.1)

使用面选择器,选择要从生成的空心形状中删除的面。

11.例子16-33 - CadQuery 中文文档

result = cq.Workplane("front").box(2, 2, 2).faces("+Z").shell(0.1)

可以使用更复杂的选择器删除多个面。 11.例子16-33 - CadQuery 中文文档

result = (
     cq.Workplane("front")
     .box(2, 2, 2)
     .faces("+Z or -X or +X")
     .shell(0.1)
)

Api References:Workplane.shell() Selectors ReferenceWorkplane.box() Workplane.faces()Workplane()

20、放样

放样是一组线扫掠得到实体。 此示例在矩形和圆形截面之间创建放样截面。

11.例子16-33 - CadQuery 中文文档

result = (
    cq.Workplane("front")
    .box(4.0, 4.0, 0.25)
    .faces(">Z")
    .circle(1.5)
    .workplane(offset=3.0)
    .rect(0.75, 0.5)
    .loft(combine=True)
)

Api References: Workplane.loft() Workplane.box()Workplane.faces()

21、挤压至指定面

有时你会想要挤出一条线直到给定的面,这个面可能不是平面,或者你可能不容易知道必须挤出的距离。 在这种情况下,可以使用 next、 last 或 Face对象 作为 extrude() 终止条件的参数 。

11.例子16-33 - CadQuery 中文文档

result = (
    cq.Workplane(origin=(20, 0, 0))
    .circle(2)
    .revolve(180, (-20, 0, 0), (-20, -1, 0))
    .center(-20, 0)
    .workplane()
    .rect(20, 4)
    .extrude("next")
)

cutBlind() 也有相同的功能,也可以一次处理多个 Wire 线对象(extrude() 也有同样的功能)。 11.例子16-33 - CadQuery 中文文档

skyscrapers_locations = [(-16, 1), (-8, 0), (7, 0.2), (17, -1.2)]
angles = iter([15, 0, -8, 10])
skyscrapers = (
    cq.Workplane()
    .pushPoints(skyscrapers_locations)
    .eachpoint(
        lambda loc: (
            cq.Workplane()
            .rect(5, 16)
            .workplane(offset=10)
            .ellipse(3, 8)
            .workplane(offset=10)
            .slot2D(20, 5, 90)
            .loft()
            .rotateAboutCenter((0, 0, 1), next(angles))
            .val()
            .located(loc)
        )
    )
)

result = (
    skyscrapers.transformed((0, -90, 0))
    .moveTo(15, 0)
    .rect(3, 3, forConstruction=True)
    .vertices()
    .circle(1)
    .cutBlind("last")
)

这是一个典型的情况,挤压和切割到给定的面非常方便。 它允许我们挤压或切割直到曲面,而不会出现重叠问题。 11.例子16-33 - CadQuery 中文文档

sphere = cq.Workplane().sphere(5)
base = cq.Workplane(origin=(0, 0, -2)).box(12, 12, 10).cut(sphere).edges("|Z").fillet(2)
sphere_face = base.faces(">>X[2] and (not |Z) and (not |Y)").val()
base = base.faces("<Z").workplane().circle(2).extrude(10)

shaft = cq.Workplane().sphere(4.5).circle(1.5).extrude(20)

spherical_joint = (
    base.union(shaft)
    .faces(">X")
    .workplane(centerOption="CenterOfMass")
    .move(0, 4)
    .slot2D(10, 2, 90)
    .cutBlind(sphere_face)
    .workplane(offset=10)
    .move(0, 2)
    .circle(0.9)
    .extrude("next")
)

result = spherical_joint
show_object(result)

::: warning 提示 如果你想要挤出的线不能完全投影到目标表面上,结果将是不可预测的。此外,负责查找候选面的算法通过计算与从线中心沿挤出方向创建的线相交的所有面来进行搜索。因此,请确保您的线可以投射到目标面上,以避免出现意外行为。 ::: Api References:Workplane.cutBlind() Workplane.rect()Workplane.ellipse() Workplane.workplane() Workplane.slot2D() Workplane.loft()Workplane.rotateAboutCenter() Workplane.transformed() Workplane.moveTo() Workplane.circle() |

22、制作沉头孔和埋头孔

沉头孔和埋头孔非常常见,以至于 CadQuery 专门创建宏以便用一个简单的步骤来创建它们。

::: primary Note

  1. 埋头孔:
    • 形状: 埋头孔在顶部具有较大直径,然后过渡到较小直径的孔。
    • 用途: 顶部较大的直径旨在容纳螺栓或螺钉的头部,而下面较小的直径是用来容纳螺纹部分的。这种设计使得紧固件的头部可以位于表面以下,提供平坦或齐平的外观。
  2. 沉头孔:
    • 形状: 沉头孔呈锥形,表面处有较宽的开口,然后逐渐变为较小直径的孔。
    • 用途: 较宽的开口设计用来容纳螺钉或螺栓的头部。锥形的形状使得头部可以与表面齐平或稍微低于表面,提供平滑和美观的外观。沉头孔通常在外观是关键考虑因素时使用。

总的来说,主要区别在于这些孔的形状和用途。埋头孔具有阶梯状的形状,适应紧固件的头部,而沉头孔具有锥形的形状,使得紧固件的头部可以与表面齐平或稍微低于表面,以获得更流畅的外观。选择取决于应用的具体要求和期望的美观效果。 :::

Workplane.hole()类似,这些函数对点列表以及单个点进行操作。

11.例子16-33 - CadQuery 中文文档

result = (
    cq.Workplane(cq.Plane.XY())
    .box(4, 2, 0.5)
    .faces(">Z")
    .workplane().tag("p1")
    .rect(3.5, 1.5, forConstruction=True)
    .vertices()
    .cboreHole(0.125, 0.25, 0.125, depth=None) # 埋头孔
)
result = (
    result.workplaneFromTagged("p1")
    .rect(2.5, 1.5, forConstruction=True)
    .vertices()
    .cskHole(0.125, 0.25, 82, depth=None)  # 沉头孔
)

Api References: Workplane.cboreHole() Workplane.cskHole() Workplane.box()Workplane.rect()Workplane.workplane()Workplane.vertices() Workplane.faces() Workplane()

23、2D 中的偏移线

可以使用Workplane.offset2D()转换二维线。 它们可以向内或向外偏移,并采用不同的技术来延伸边角。 11.例子16-33 - CadQuery 中文文档

original = cq.Workplane().polygon(5, 10).extrude(0.1).translate((0, 0, 2))
arc = cq.Workplane().polygon(5, 10).offset2D(1, "arc").extrude(0.1).translate((0, 0, 1))
intersection = cq.Workplane().polygon(5, 10).offset2D(1, "intersection").extrude(0.1)
result = original.add(arc).add(intersection)

使用 forConstruction 参数,你可以执行从对象轮廓偏移一系列螺栓孔的常见任务。 这是上面的埋头孔示例,但螺栓孔是从边缘偏离的。 11.例子16-33 - CadQuery 中文文档

result = (
    cq.Workplane()
    .box(4, 2, 0.5)
    .faces(">Z")
    .edges()
    .toPending()
    .offset2D(-0.25, forConstruction=True)
    .vertices()
    .cboreHole(0.125, 0.25, 0.125, depth=None)
)

请注意, Workplane.edges() 用于选择对象。 它不会将选定的边缘添加到建模上下文中的待定边缘中,因为这会导致你的下一次挤压包括你选择的所有内容,出了绘制的线条外。 如果希望在 Workplane.offset2D() 中使用这些边,可以调用 Workplane.toPending() 以明确地将它们放入待定边缘列表中。

Api References: Workplane.offset2D() Workplane.cboreHole() Workplane.cskHole() Workplane.box()Workplane.polygon()Workplane.workplane() Workplane.vertices() Workplane.edges() Workplane.faces() Workplane()

24、圆角

圆角是通过选择实体的边缘并使用 fillet() 功能来完成的。

在这里,我们对一个简单平板的所有边缘进行圆角处理。 11.例子16-33 - CadQuery 中文文档

result = cq.Workplane("XY").box(3, 3, 0.5).edges("|Z").fillet(0.125)

Api References:Workplane.fillet() Workplane.box() Workplane.edges() Workplane()

25、标记对象

Workplane.tag() 方法可用于用字符串标记链中的特定对象,以便稍后在链中引用它。

Workplane.workplaneFromTagged() 方法将应用 Workplane.copyWorkplane() 复制标记对象后返回。 例如,当从同一个表面挤出两个不同的实体时,在第一个实体被挤出后,很难用 CadQuery 的其他选择器重新选择原始表面。

11.例子16-33 - CadQuery 中文文档

result = (
    cq.Workplane("XY")
    # create and tag the base workplane
    .box(10, 10, 10)
    .faces(">Z")
    .workplane()
    .tag("baseplane")
    # extrude a cylinder
    .center(-3, 0)
    .circle(1)
    .extrude(3)
    # to reselect the base workplane, simply
    .workplaneFromTagged("baseplane")
    # extrude a second cylinder
    .center(3, 0)
    .circle(1)
    .extrude(2)
)

标签也可以与大多数选择器一起使用,包括Workplane.vertices(), Workplane.faces(), Workplane.edges(), Workplane.wires(), Workplane.shells(), Workplane.solids() and Workplane.compounds()11.例子16-33 - CadQuery 中文文档

result = (
    cq.Workplane("XY")
    # create a triangular prism and tag it
    .polygon(3, 5)
    .extrude(4)
    .tag("prism")
    # create a sphere that obscures the prism
    .sphere(10)
    # create features based on the prism's faces
    .faces("<X", tag="prism")
    .workplane()
    .circle(1)
    .cutThruAll()
    .faces(">X", tag="prism")
    .faces(">Y")
    .workplane()
    .circle(1)
    .cutThruAll()
)

Api References: Workplane.tag() Workplane.getTagged()Workplane.workplaneFromTagged() Workplane.extrude() Workplane.cutThruAll() Workplane.circle() Workplane.faces()Workplane()

26、参数化轴承座

结合一些基本功能,只需几行代码,就可以制作出非常好的参数化轴承座。 11.例子16-33 - CadQuery 中文文档

(length, height, bearing_diam, thickness, padding) = (30.0, 40.0, 22.0, 10.0, 8.0)

result = (
    cq.Workplane("XY")
    .box(length, height, thickness)
    .faces(">Z")
    .workplane()
    .hole(bearing_diam)
    .faces(">Z")
    .workplane()
    .rect(length - padding, height - padding, forConstruction=True)
    .vertices()
    .cboreHole(2.4, 4.4, 2.1)
)

27、分割对象

可以使用工作平面分割对象,并保留其中一个或两个部分。 11.例子16-33 - CadQuery 中文文档

c = cq.Workplane("XY").box(1, 1, 1).faces(">Z").workplane().circle(0.25).cutThruAll()

# now cut it in half sideways
result = c.faces(">Y").workplane(-0.5).split(keepTop=True).clean()
show_object(result)

Api References: Workplane.split() Workplane.box()Workplane.circle() Workplane.cutThruAll() Workplane.workplane() Workplane()

28、经典 OCC 瓶

CadQuery 基于 OpenCascade.org (OCC) 建模内核。 熟悉 OCC 的人都知道著名的瓶子示例。The bottle example in the OCCT online documentation.

对应 pythonOCC 版本示例在 这里

当然,此示例与 OCC 版本之间的一个区别是长度。 这个示例是 13 行的较长示例之一,但与 pythonOCC 版本相比非常短,pythonOCC长10 倍! 11.例子16-33 - CadQuery 中文文档

(L, w, t) = (20.0, 6.0, 3.0)
s = cq.Workplane("XY")

# Draw half the profile of the bottle and extrude it
p = (
    s.center(-L / 2.0, 0)
    .vLine(w / 2.0)
    .threePointArc((L / 2.0, w / 2.0 + t), (L, w / 2.0))
    .vLine(-w / 2.0)
    .mirrorX()
    .extrude(30.0, True)
)

# Make the neck
p = p.faces(">Z").workplane(centerOption="CenterOfMass").circle(3.0).extrude(2.0, True)

# Make a shell
result = p.faces(">Z").shell(0.3)
show_object(result)

Api References: Workplane.extrude() Workplane.mirrorX() Workplane.threePointArc() Workplane.workplane() Workplane.vertices() Workplane.vLine() Workplane.faces() Workplane()

29、参数化外壳

11.例子16-33 - CadQuery 中文文档

# parameter definitions
p_outerWidth = 100.0  # Outer width of box enclosure
p_outerLength = 150.0  # Outer length of box enclosure
p_outerHeight = 50.0  # Outer height of box enclosure

p_thickness = 3.0  # Thickness of the box walls
p_sideRadius = 10.0  # Radius for the curves around the sides of the box
p_topAndBottomRadius = (
    2.0  # Radius for the curves on the top and bottom edges of the box
)

p_screwpostInset = 12.0  # How far in from the edges the screw posts should be place.
p_screwpostID = 4.0  # Inner Diameter of the screw post holes, should be roughly screw diameter not including threads
p_screwpostOD = 10.0  # Outer Diameter of the screw posts.\nDetermines overall thickness of the posts

p_boreDiameter = 8.0  # Diameter of the counterbore hole, if any
p_boreDepth = 1.0  # Depth of the counterbore hole, if
p_countersinkDiameter = 0.0  # Outer diameter of countersink. Should roughly match the outer diameter of the screw head
p_countersinkAngle = 90.0  # Countersink angle (complete angle between opposite sides, not from center to one side)
p_flipLid = True  # Whether to place the lid with the top facing down or not.
p_lipHeight = 1.0  # Height of lip on the underside of the lid.\nSits inside the box body for a snug fit.

# outer shell
oshell = (
    cq.Workplane("XY")
    .rect(p_outerWidth, p_outerLength)
    .extrude(p_outerHeight + p_lipHeight)
)

# weird geometry happens if we make the fillets in the wrong order
if p_sideRadius > p_topAndBottomRadius:
    oshell = oshell.edges("|Z").fillet(p_sideRadius)
    oshell = oshell.edges("#Z").fillet(p_topAndBottomRadius)
else:
    oshell = oshell.edges("#Z").fillet(p_topAndBottomRadius)
    oshell = oshell.edges("|Z").fillet(p_sideRadius)

# inner shell
ishell = (
    oshell.faces("<Z")
    .workplane(p_thickness, True)
    .rect((p_outerWidth - 2.0 * p_thickness), (p_outerLength - 2.0 * p_thickness))
    .extrude(
        (p_outerHeight - 2.0 * p_thickness), False
    )  # set combine false to produce just the new boss
)
ishell = ishell.edges("|Z").fillet(p_sideRadius - p_thickness)

# make the box outer box
box = oshell.cut(ishell)

# make the screw posts
POSTWIDTH = p_outerWidth - 2.0 * p_screwpostInset
POSTLENGTH = p_outerLength - 2.0 * p_screwpostInset

box = (
    box.faces(">Z")
    .workplane(-p_thickness)
    .rect(POSTWIDTH, POSTLENGTH, forConstruction=True)
    .vertices()
    .circle(p_screwpostOD / 2.0)
    .circle(p_screwpostID / 2.0)
    .extrude(-1.0 * (p_outerHeight + p_lipHeight - p_thickness), True)
)

# split lid into top and bottom parts
(lid, bottom) = (
    box.faces(">Z")
    .workplane(-p_thickness - p_lipHeight)
    .split(keepTop=True, keepBottom=True)
    .all()
)  # splits into two solids

# translate the lid, and subtract the bottom from it to produce the lid inset
lowerLid = lid.translate((0, 0, -p_lipHeight))
cutlip = lowerLid.cut(bottom).translate(
    (p_outerWidth + p_thickness, 0, p_thickness - p_outerHeight + p_lipHeight)
)

# compute centers for screw holes
topOfLidCenters = (
    cutlip.faces(">Z")
    .workplane(centerOption="CenterOfMass")
    .rect(POSTWIDTH, POSTLENGTH, forConstruction=True)
    .vertices()
)

# add holes of the desired type
if p_boreDiameter > 0 and p_boreDepth > 0:
    topOfLid = topOfLidCenters.cboreHole(
        p_screwpostID, p_boreDiameter, p_boreDepth, 2.0 * p_thickness
    )
elif p_countersinkDiameter > 0 and p_countersinkAngle > 0:
    topOfLid = topOfLidCenters.cskHole(
        p_screwpostID, p_countersinkDiameter, p_countersinkAngle, 2.0 * p_thickness
    )
else:
    topOfLid = topOfLidCenters.hole(p_screwpostID, 2.0 * p_thickness)

# flip lid upside down if desired
if p_flipLid:
    topOfLid = topOfLid.rotateAboutCenter((1, 0, 0), 180)

# return the combined result
result = topOfLid.union(bottom)
show_object(result)

Api References: Workplane.circle()Workplane.rect()Workplane.extrude() Workplane.box()Workplane.all()Workplane.faces() Workplane.vertices()Workplane.edges() Workplane.workplane()Workplane.fillet()Workplane.cut() Workplane.union()Workplane.rotateAboutCenter()Workplane.cboreHole()Workplane.cskHole()Workplane.hole()

30、乐高积木

此脚本将生成任何尺寸的常规矩形 Lego™ 积木。 唯一棘手的是关于砖块底部的逻辑。 11.例子16-33 - CadQuery 中文文档

#####
# Inputs
######
lbumps = 6  # number of bumps long
wbumps = 2  # number of bumps wide
thin = True  # True for thin, False for thick

#
# Lego Brick Constants-- these make a Lego brick a Lego :)
#
pitch = 8.0
clearance = 0.1
bumpDiam = 4.8
bumpHeight = 1.8
if thin:
    height = 3.2
else:
    height = 9.6

t = (pitch - (2 * clearance) - bumpDiam) / 2.0
postDiam = pitch - t  # works out to 6.5
total_length = lbumps * pitch - 2.0 * clearance
total_width = wbumps * pitch - 2.0 * clearance

# make the base
s = cq.Workplane("XY").box(total_length, total_width, height)

# shell inwards not outwards
s = s.faces("<Z").shell(-1.0 * t)

# make the bumps on the top
s = (
    s.faces(">Z")
    .workplane()
    .rarray(pitch, pitch, lbumps, wbumps, True)
    .circle(bumpDiam / 2.0)
    .extrude(bumpHeight)
)

# add posts on the bottom. posts are different diameter depending on geometry
# solid studs for 1 bump, tubes for multiple, none for 1x1
tmp = s.faces("<Z").workplane(invert=True)

if lbumps > 1 and wbumps > 1:
    tmp = (
        tmp.rarray(pitch, pitch, lbumps - 1, wbumps - 1, center=True)
        .circle(postDiam / 2.0)
        .circle(bumpDiam / 2.0)
        .extrude(height - t)
    )
elif lbumps > 1:
    tmp = (
        tmp.rarray(pitch, pitch, lbumps - 1, 1, center=True)
        .circle(t)
        .extrude(height - t)
    )
elif wbumps > 1:
    tmp = (
        tmp.rarray(pitch, pitch, 1, wbumps - 1, center=True)
        .circle(t)
        .extrude(height - t)
    )
else:
    tmp = s
show_object(tmp)

31、盲文示例

11.例子16-33 - CadQuery 中文文档

from collections import namedtuple


# text_lines is a list of text lines.
# Braille (converted with braille-converter:
# https://github.com/jpaugh/braille-converter.git).
text_lines = ['⠠ ⠋ ⠗ ⠑ ⠑ ⠠ ⠉ ⠠ ⠁ ⠠ ⠙']
# See http://www.tiresias.org/research/reports/braille_cell.htm for examples
# of braille cell geometry.
horizontal_interdot = 2.5
vertical_interdot = 2.5
horizontal_intercell = 6
vertical_interline = 10
dot_height = 0.5
dot_diameter = 1.3

base_thickness = 1.5

# End of configuration.
BrailleCellGeometry = namedtuple('BrailleCellGeometry',
                                 ('horizontal_interdot',
                                  'vertical_interdot',
                                  'intercell',
                                  'interline',
                                  'dot_height',
                                  'dot_diameter'))


class Point(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __add__(self, other):
        return Point(self.x + other.x, self.y + other.y)

    def __len__(self):
        return 2

    def __getitem__(self, index):
        return (self.x, self.y)[index]

    def __str__(self):
        return '({}, {})'.format(self.x, self.y)


def brailleToPoints(text, cell_geometry):
    # Unicode bit pattern (cf. https://en.wikipedia.org/wiki/Braille_Patterns).
    mask1 = 0b00000001
    mask2 = 0b00000010
    mask3 = 0b00000100
    mask4 = 0b00001000
    mask5 = 0b00010000
    mask6 = 0b00100000
    mask7 = 0b01000000
    mask8 = 0b10000000
    masks = (mask1, mask2, mask3, mask4, mask5, mask6, mask7, mask8)

    # Corresponding dot position
    w = cell_geometry.horizontal_interdot
    h = cell_geometry.vertical_interdot
    pos1 = Point(0, 2 * h)
    pos2 = Point(0, h)
    pos3 = Point(0, 0)
    pos4 = Point(w, 2 * h)
    pos5 = Point(w, h)
    pos6 = Point(w, 0)
    pos7 = Point(0, -h)
    pos8 = Point(w, -h)
    pos = (pos1, pos2, pos3, pos4, pos5, pos6, pos7, pos8)

    # Braille blank pattern (u'\u2800').
    blank = '⠀'
    points = []
    # Position of dot1 along the x-axis (horizontal).
    character_origin = 0
    for c in text:
        for m, p in zip(masks, pos):
            delta_to_blank = ord(c) - ord(blank)
            if (m & delta_to_blank):
                points.append(p + Point(character_origin, 0))
        character_origin += cell_geometry.intercell
    return points


def get_plate_height(text_lines, cell_geometry):
    # cell_geometry.vertical_interdot is also used as space between base
    # borders and characters.
    return (2 * cell_geometry.vertical_interdot +
            2 * cell_geometry.vertical_interdot +
            (len(text_lines) - 1) * cell_geometry.interline)


def get_plate_width(text_lines, cell_geometry):
    # cell_geometry.horizontal_interdot is also used as space between base
    # borders and characters.
    max_len = max([len(t) for t in text_lines])
    return (2 * cell_geometry.horizontal_interdot +
            cell_geometry.horizontal_interdot +
            (max_len - 1) * cell_geometry.intercell)


def get_cylinder_radius(cell_geometry):
    """Return the radius the cylinder should have
    The cylinder have the same radius as the half-sphere make the dots (the
    hidden and the shown part of the dots).
    The radius is such that the spherical cap with diameter
    cell_geometry.dot_diameter has a height of cell_geometry.dot_height.
    """
    h = cell_geometry.dot_height
    r = cell_geometry.dot_diameter / 2
    return (r ** 2 + h ** 2) / 2 / h


def get_base_plate_thickness(plate_thickness, cell_geometry):
    """Return the height on which the half spheres will sit"""
    return (plate_thickness +
            get_cylinder_radius(cell_geometry) -
            cell_geometry.dot_height)


def make_base(text_lines, cell_geometry, plate_thickness):
    base_width = get_plate_width(text_lines, cell_geometry)
    base_height = get_plate_height(text_lines, cell_geometry)
    base_thickness = get_base_plate_thickness(plate_thickness, cell_geometry)
    base = cq.Workplane('XY').box(base_width, base_height, base_thickness,
                                  centered=False)
    return base


def make_embossed_plate(text_lines, cell_geometry):
    """Make an embossed plate with dots as spherical caps
    Method:
        - make a thin plate on which sit cylinders
        - fillet the upper edge of the cylinders so to get pseudo half-spheres
        - make the union with a thicker plate so that only the sphere caps stay
          "visible".
    """
    base = make_base(text_lines, cell_geometry, base_thickness)

    dot_pos = []
    base_width = get_plate_width(text_lines, cell_geometry)
    base_height = get_plate_height(text_lines, cell_geometry)
    y = base_height - 3 * cell_geometry.vertical_interdot
    line_start_pos = Point(cell_geometry.horizontal_interdot, y)
    for text in text_lines:
        dots = brailleToPoints(text, cell_geometry)
        dots = [p + line_start_pos for p in dots]
        dot_pos += dots
        line_start_pos += Point(0, -cell_geometry.interline)

    r = get_cylinder_radius(cell_geometry)
    base = (base.faces('>Z').vertices('<XY').workplane()
        .pushPoints(dot_pos).circle(r)
        .extrude(r))
    # Make a fillet almost the same radius to get a pseudo spherical cap.
    base = (base.faces('>Z').edges()
        .fillet(r - 0.001))
    hidding_box = cq.Workplane('XY').box(
        base_width, base_height, base_thickness, centered=False)
    result = hidding_box.union(base)
    return result

_cell_geometry = BrailleCellGeometry(
    horizontal_interdot,
    vertical_interdot,
    horizontal_intercell,
    vertical_interline,
    dot_height,
    dot_diameter)

if base_thickness < get_cylinder_radius(_cell_geometry):
    raise ValueError('Base thickness should be at least {}'.format(dot_height))

result = make_embossed_plate(text_lines, _cell_geometry)

32、带各种连接孔的面板

11.例子16-33 - CadQuery 中文文档

# The dimensions of the model. These can be modified rather than changing the
# object's code directly.
width = 400
height = 500
thickness = 2

# Create a plate with two polygons cut through it
result = cq.Workplane("front").box(width, height, thickness)

h_sep = 60
for idx in range(4):
    result = (
        result.workplane(offset=1, centerOption="CenterOfBoundBox")
        .center(157, 210 - idx * h_sep)
        .moveTo(-23.5, 0)
        .circle(1.6)
        .moveTo(23.5, 0)
        .circle(1.6)
        .moveTo(-17.038896, -5.7)
        .threePointArc((-19.44306, -4.70416), (-20.438896, -2.3))
        .lineTo(-21.25, 2.3)
        .threePointArc((-20.25416, 4.70416), (-17.85, 5.7))
        .lineTo(17.85, 5.7)
        .threePointArc((20.25416, 4.70416), (21.25, 2.3))
        .lineTo(20.438896, -2.3)
        .threePointArc((19.44306, -4.70416), (17.038896, -5.7))
        .close()
        .cutThruAll()
    )

for idx in range(4):
    result = (
        result.workplane(offset=1, centerOption="CenterOfBoundBox")
        .center(157, -30 - idx * h_sep)
        .moveTo(-16.65, 0)
        .circle(1.6)
        .moveTo(16.65, 0)
        .circle(1.6)
        .moveTo(-10.1889, -5.7)
        .threePointArc((-12.59306, -4.70416), (-13.5889, -2.3))
        .lineTo(-14.4, 2.3)
        .threePointArc((-13.40416, 4.70416), (-11, 5.7))
        .lineTo(11, 5.7)
        .threePointArc((13.40416, 4.70416), (14.4, 2.3))
        .lineTo(13.5889, -2.3)
        .threePointArc((12.59306, -4.70416), (10.1889, -5.7))
        .close()
        .cutThruAll()
    )

h_sep4DB9 = 30
for idx in range(8):
    result = (
        result.workplane(offset=1, centerOption="CenterOfBoundBox")
        .center(91, 225 - idx * h_sep4DB9)
        .moveTo(-12.5, 0)
        .circle(1.6)
        .moveTo(12.5, 0)
        .circle(1.6)
        .moveTo(-6.038896, -5.7)
        .threePointArc((-8.44306, -4.70416), (-9.438896, -2.3))
        .lineTo(-10.25, 2.3)
        .threePointArc((-9.25416, 4.70416), (-6.85, 5.7))
        .lineTo(6.85, 5.7)
        .threePointArc((9.25416, 4.70416), (10.25, 2.3))
        .lineTo(9.438896, -2.3)
        .threePointArc((8.44306, -4.70416), (6.038896, -5.7))
        .close()
        .cutThruAll()
    )

for idx in range(4):
    result = (
        result.workplane(offset=1, centerOption="CenterOfBoundBox")
        .center(25, 210 - idx * h_sep)
        .moveTo(-23.5, 0)
        .circle(1.6)
        .moveTo(23.5, 0)
        .circle(1.6)
        .moveTo(-17.038896, -5.7)
        .threePointArc((-19.44306, -4.70416), (-20.438896, -2.3))
        .lineTo(-21.25, 2.3)
        .threePointArc((-20.25416, 4.70416), (-17.85, 5.7))
        .lineTo(17.85, 5.7)
        .threePointArc((20.25416, 4.70416), (21.25, 2.3))
        .lineTo(20.438896, -2.3)
        .threePointArc((19.44306, -4.70416), (17.038896, -5.7))
        .close()
        .cutThruAll()
    )

for idx in range(4):
    result = (
        result.workplane(offset=1, centerOption="CenterOfBoundBox")
        .center(25, -30 - idx * h_sep)
        .moveTo(-16.65, 0)
        .circle(1.6)
        .moveTo(16.65, 0)
        .circle(1.6)
        .moveTo(-10.1889, -5.7)
        .threePointArc((-12.59306, -4.70416), (-13.5889, -2.3))
        .lineTo(-14.4, 2.3)
        .threePointArc((-13.40416, 4.70416), (-11, 5.7))
        .lineTo(11, 5.7)
        .threePointArc((13.40416, 4.70416), (14.4, 2.3))
        .lineTo(13.5889, -2.3)
        .threePointArc((12.59306, -4.70416), (10.1889, -5.7))
        .close()
        .cutThruAll()
    )

for idx in range(8):
    result = (
        result.workplane(offset=1, centerOption="CenterOfBoundBox")
        .center(-41, 225 - idx * h_sep4DB9)
        .moveTo(-12.5, 0)
        .circle(1.6)
        .moveTo(12.5, 0)
        .circle(1.6)
        .moveTo(-6.038896, -5.7)
        .threePointArc((-8.44306, -4.70416), (-9.438896, -2.3))
        .lineTo(-10.25, 2.3)
        .threePointArc((-9.25416, 4.70416), (-6.85, 5.7))
        .lineTo(6.85, 5.7)
        .threePointArc((9.25416, 4.70416), (10.25, 2.3))
        .lineTo(9.438896, -2.3)
        .threePointArc((8.44306, -4.70416), (6.038896, -5.7))
        .close()
        .cutThruAll()
    )

for idx in range(4):
    result = (
        result.workplane(offset=1, centerOption="CenterOfBoundBox")
        .center(-107, 210 - idx * h_sep)
        .moveTo(-23.5, 0)
        .circle(1.6)
        .moveTo(23.5, 0)
        .circle(1.6)
        .moveTo(-17.038896, -5.7)
        .threePointArc((-19.44306, -4.70416), (-20.438896, -2.3))
        .lineTo(-21.25, 2.3)
        .threePointArc((-20.25416, 4.70416), (-17.85, 5.7))
        .lineTo(17.85, 5.7)
        .threePointArc((20.25416, 4.70416), (21.25, 2.3))
        .lineTo(20.438896, -2.3)
        .threePointArc((19.44306, -4.70416), (17.038896, -5.7))
        .close()
        .cutThruAll()
    )

for idx in range(4):
    result = (
        result.workplane(offset=1, centerOption="CenterOfBoundBox")
        .center(-107, -30 - idx * h_sep)
        .circle(14)
        .rect(24.7487, 24.7487, forConstruction=True)
        .vertices()
        .hole(3.2)
        .cutThruAll()
    )

for idx in range(8):
    result = (
        result.workplane(offset=1, centerOption="CenterOfBoundBox")
        .center(-173, 225 - idx * h_sep4DB9)
        .moveTo(-12.5, 0)
        .circle(1.6)
        .moveTo(12.5, 0)
        .circle(1.6)
        .moveTo(-6.038896, -5.7)
        .threePointArc((-8.44306, -4.70416), (-9.438896, -2.3))
        .lineTo(-10.25, 2.3)
        .threePointArc((-9.25416, 4.70416), (-6.85, 5.7))
        .lineTo(6.85, 5.7)
        .threePointArc((9.25416, 4.70416), (10.25, 2.3))
        .lineTo(9.438896, -2.3)
        .threePointArc((8.44306, -4.70416), (6.038896, -5.7))
        .close()
        .cutThruAll()
    )

for idx in range(4):
    result = (
        result.workplane(offset=1, centerOption="CenterOfBoundBox")
        .center(-173, -30 - idx * h_sep)
        .moveTo(-2.9176, -5.3)
        .threePointArc((-6.05, 0), (-2.9176, 5.3))
        .lineTo(2.9176, 5.3)
        .threePointArc((6.05, 0), (2.9176, -5.3))
        .close()
        .cutThruAll()
    )

33、摆线齿轮

可以使用 parametricCurve 定义复杂的几何图形。 本实例生成螺旋摆线齿轮 11.例子16-33 - CadQuery 中文文档

import cadquery as cq
from math import sin, cos, pi, floor


# define the generating function
def hypocycloid(t, r1, r2):
    return (
        (r1 - r2) * cos(t) + r2 * cos(r1 / r2 * t - t),
        (r1 - r2) * sin(t) + r2 * sin(-(r1 / r2 * t - t)),
    )


def epicycloid(t, r1, r2):
    return (
        (r1 + r2) * cos(t) - r2 * cos(r1 / r2 * t + t),
        (r1 + r2) * sin(t) - r2 * sin(r1 / r2 * t + t),
    )


def gear(t, r1=4, r2=1):
    if (-1) ** (1 + floor(t / 2 / pi * (r1 / r2))) < 0:
        return epicycloid(t, r1, r2)
    else:
        return hypocycloid(t, r1, r2)


# create the gear profile and extrude it
result = (
    cq.Workplane("XY")
    .parametricCurve(lambda t: gear(t * 2 * pi, 6, 1))
    .twistExtrude(15, 90)
    .faces(">Z")
    .workplane()
    .circle(2)
    .cutThruAll()
)

文章来源:CadQuery 中文文档