fastener - parametric threaded fasteners

Many mechanical designs will contain threaded fasteners of some kind, either in a threaded hole or threaded screws or bolts holding two or more parts together. The fastener sub-package provides a set of classes that create many different types of nuts, screws and washers - as follows:

fastener_disc

The holes for the screws in this figure were created with an extension of the Workplane class clearanceHole(), the nuts tapHole() and the central hole threadedHole(). The washers were automatically placed and all components were add to an Assembly in their correct position and orientations - see Custom Holes for details.

Here is a list of the classes (and fastener types) provided:

  • Nut - the base nut class

    • BradTeeNut: Hilitchi

    • DomedCapNut: din1587

    • HeatSetNut: McMaster-Carr, Hilitchi

    • HexNut: iso4033, iso4035, iso4032

    • HexNutWithFlange: din1665

    • UnchamferedHexagonNut: iso4036

    • SquareNut: din557

  • Screw - the base screw class

    • ButtonHeadScrew: iso7380_1

    • ButtonHeadWithCollarScrew: iso7380_2

    • CheeseHeadScrew: iso14580, iso7048, iso1207

    • CounterSunkScrew: iso2009, iso14582, iso14581, iso10642, iso7046

    • HexHeadScrew: iso4017, din931, iso4014

    • HexHeadWithFlangeScrew: din1662, din1665

    • PanHeadScrew: asme_b_18.6.3, iso1580, iso14583

    • PanHeadWithCollarScrew: din967

    • RaisedCheeseHeadScrew: iso7045

    • RaisedCounterSunkOvalHeadScrew: iso2010, iso7047, iso14584

    • SetScrew: iso4026

    • SocketHeadCapScrew: iso4762, asme_b18.3

  • Washer - the base washer class

    • PlainWasher: iso7094, iso7093, iso7089, iso7091

    • ChamferedWasher: iso7090

    • CheeseHeadWasher: iso7092

See Extending the fastener sub-package for guidance on how to easily add new sizes or entirely new types of fasteners.

The following example creates a variety of different sized fasteners:

import cadquery as cq
from cq_warehouse.fastener import HexNut, SocketHeadCapScrew, SetScrew
MM = 1
IN = 25.4 * MM

nut = HexNut(size="M3-0.5", fastener_type="iso4032")
setscrew = SetScrew(size="M6-1", fastener_type="iso4026",length=10 * MM)
capscrew = SocketHeadCapScrew(size="#6-32", fastener_type="asme_b18.3", length=(1/2) * IN)

Both metric and imperial sized standard fasteners are directly supported by the fastener sub-package although the majority of the fasteners currently implemented are metric.

Many of the fastener standards provide ranges for some of the dimensions - for example a minimum and maximum head diameter. This sub-package generally uses the maximum sizes when a range is available in-order to ensure clearance between a fastener and another part won’t be compromised by a physical part that is within specification but larger than the CAD model.

Threaded parts are complex for CAD systems to create and significantly increase the storage requirements thus making the system slow and difficult to use. To minimize these requirements all of the fastener classes have a simple boolean parameter that when True doesn’t create actual threads at all. Such simple parts have the same overall dimensions and such that they can be used to check for fitment without dramatically impacting performance.

Hint

⌛CQ-editor⌛ You can increase the Preferences→3D Viewer→Deviation parameter to improve performance by slightly compromising accuracy.

All of the fasteners default to right-handed thread but each of them provide a hand string parameter which can either be "right" or "left".

Deprecated since version 0.8.0: Previous versions of cq_warehouse required the used of a cq_object instance variable to access the CadQuery cad object. Currently all fastener objects are a sub-class of the CadQuery Solid object and therefore can be used as any other Solid object without referencing cq_object. Future versions of cq_warehouse will remove cq_object entirely.

The following sections describe each of the provided classes.

Nut

As the base class of all other nut and bolt classes, all of the derived nut classes share the same interface as follows:

class Nut(size, fastener_type, hand='right', simple=True)

Parametric Nut

Base Class used to create standard threaded nuts

Parameters
  • size (str) – standard sizes - e.g. “M6-1”

  • fastener_type (str) – type identifier - e.g. “iso4032”

  • hand (Literal["right","left"], optional) – thread direction. Defaults to “right”.

  • simple (bool, optional) – simplify by not creating thread. Defaults to True.

Raises
  • ValueError – invalid size, must be formatted as size-pitch or size-TPI

  • ValueError – invalid fastener_type

  • ValueError – invalid hand, must be one of ‘left’ or ‘right’

  • ValueError – invalid size

Each nut instance creates a set of instance variables that provide the CAD object as well as valuable parameters, as follows (values intended for internal use are not shown):

Variables
  • tap_drill_sizes (dict) – dictionary of drill sizes for tapped holes

  • tap_hole_diameters (dict) – dictionary of drill diameters for tapped holes

  • clearance_drill_sizes (dict) – dictionary of drill sizes for clearance holes

  • clearance_hole_diameters (dict) – dictionary of drill diameters for clearance holes

  • info (str) – identifying information

  • nut_class (class) – the derived class that created this nut

  • nut_thickness (float) – maximum thickness of the nut

  • nut_diameter (float) – maximum diameter of the nut

Nut Selection

As there are many classes and types of nuts to select from, the Nut class provides some methods that can help find the correct nut for your application. As a reminder, to find the subclasses of the Nut class, use __subclasses__():

>>> Nut.__subclasses__()
[<class 'cq_warehouse.fastener.DomedCapNut'>, ...]

Here is a summary of the class methods:

classmethod Nut.types()

Return a set of the nut types

Return type

List[str]

>>> HexNut.types()
{'iso4033', 'iso4032', 'iso4035'}
classmethod Nut.sizes(fastener_type)

Return a list of the nut sizes for the given type

Return type

List[str]

>>> HexNut.sizes("iso4033")
['M1.6-0.35', 'M1.8-0.35', 'M2-0.4', 'M2.5-0.45', 'M3-0.45', 'M3.5-0.6', 'M4-0.7', 'M5-0.8', 'M6-1', 'M8-1.25', 'M10-1.5', 'M12-1.75', 'M14-2', 'M16-2', 'M18-2.5', 'M20-2.5', 'M22-2.5', 'M24-3', 'M27-3', 'M30-3.5', 'M33-3.5', 'M36-4', 'M39-4', 'M42-4.5', 'M45-4.5', 'M48-5', 'M52-5']
classmethod Nut.select_by_size(size)

Return a dictionary of list of fastener types of this size

Return type

dict

>>> Nut.select_by_size("M6-1")
{<class 'cq_warehouse.fastener.DomedCapNut'>: ['din1587'], <class 'cq_warehouse.fastener.HexNut'>: ['iso4035', 'iso4032', 'iso4033'], <class 'cq_warehouse.fastener.HexNutWithFlange'>: ['din1665'], <class 'cq_warehouse.fastener.UnchamferedHexagonNut'>: ['iso4036'], <class 'cq_warehouse.fastener.SquareNut'>: ['din557']}

Derived Nut Classes

The following is a list of the current nut classes derived from the base Nut class. Also listed is the type for each of these derived classes where the type refers to a standard that defines the nut parameters. All derived nuts inherit the same API as the base Nut class.

  • BradTeeNut: Hilitchi

  • DomedCapNut: din1587

  • HeatSetNut: McMaster-Carr, Hilitchi

  • HexNut: iso4033, iso4035, iso4032

  • HexNutWithFlange: din1665

  • UnchamferedHexagonNut: iso4036

  • SquareNut: din557

Detailed information about any of the nut types can be readily found on the internet from manufacture’s websites or from the standard document itself.

The BradTeeNut is a compound object that uses multiple CounterSunkScrew to fix the nut to the base object. The size of these brads is stored in the nut_data instance variable and can be used to place the brads as shown in the brad_tee_and_heatset_nuts.py example.

"""

Brad Tee and HeatSet Nuts Example

name: brad_tee_and_heatset_nuts.py
by:   Gumyr
date: February 28th 2022

desc: Example of using the BradTee and HeatSet nuts with pushFastenerLocations()
      method to align holes.

license:

    Copyright 2022 Gumyr

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.

"""
import cadquery as cq
from cq_warehouse.fastener import BradTeeNut, CounterSunkScrew, HeatSetNut
import cq_warehouse.extensions

MM = 1

# Create the fasteners used in this example
bradtee_nut = BradTeeNut(size="M8-1.25", fastener_type="Hilitchi", simple=False)
brad = CounterSunkScrew(
    size=bradtee_nut.nut_data["brad_size"],
    length=20 * MM,
    fastener_type="iso10642",
    simple=False,
)
heatset = HeatSetNut(
    size=bradtee_nut.nut_data["brad_size"] + "-Standard",
    fastener_type="McMaster-Carr",
    simple=True,
)
# Create an empty Assembly to hold all of the fasteners
fastener_assembly = cq.Assembly(None, name="plate")

# Create a simple plate with appropriate holes to house all the fasteners
plate_size = (50 * MM, 50 * MM, 20 * MM)
plate = (
    cq.Workplane("XY")
    .box(*plate_size, centered=(True, True, False))
    .faces(">Z")
    .workplane()
    .clearanceHole(fastener=bradtee_nut, baseAssembly=fastener_assembly)
    .polarArray(
        bradtee_nut.nut_data["bcd"] / 2, 0, 360, bradtee_nut.nut_data["brad_num"]
    )
    .clearanceHole(fastener=brad, baseAssembly=fastener_assembly)
    # Place HeatSetNuts for the brads on the bottom of the plate
    .pushFastenerLocations(
        fastener=brad,
        baseAssembly=fastener_assembly,
        offset=-plate_size[2],
        flip=True,
    )
    .insertHole(fastener=heatset, baseAssembly=fastener_assembly)
)
print(fastener_assembly.fastenerQuantities())
print(HeatSetNut.sizes("McMaster-Carr"))

if "show_object" in locals():
    show_object(plate, name="plate", options={"alpha": 0.8})
    show_object(fastener_assembly, name="fastener_assembly")
{'BradTeeNut(Hilitchi): M8-1.25': 1, 'HeatSetNut(McMaster-Carr): M4-0.7': 3, 'CounterSunkScrew(iso10642): M4-0.7x20': 3}
['M2-0.4-Short', 'M2-0.4-Standard', 'M3-0.5-Short', 'M3-0.5-Standard', 'M4-0.7-Short', 'M4-0.7-Standard', 'M5-0.8-Short', 'M5-0.8-Standard']
_images/brad_tee_nut_assembly.png

Note that a HeatSetNut can only be placed with an insertHole() method (see the Custom Holes section of more information). Also note that the size of a HeatSetNut includes a length component like “-Standard” or “-Short” but this depends on the type. Use the sizes method to see the valid values.

>>> HeatSetNut.sizes("McMaster-Carr")
['M2-0.4-Short', 'M2-0.4-Standard', 'M3-0.5-Short', 'M3-0.5-Standard', 'M4-0.7-Short', 'M4-0.7-Standard', 'M5-0.8-Short', 'M5-0.8-Standard']

Screw

As the base class of all other screw and bolt classes, all of the derived screw classes share the same interface as follows:

class Screw(size, length, fastener_type, hand='right', simple=True, socket_clearance=6)

Parametric Screw

Base class for a set of threaded screws or bolts

Parameters
  • size (str) – standard sizes - e.g. “M6-1”

  • length (float) – distance from base of head to tip of thread

  • fastener_type (str) – type identifier - e.g. “iso4014”

  • hand (Literal["right","left"], optional) – thread direction. Defaults to “right”.

  • simple (bool, optional) – simplify by not creating thread. Defaults to True.

  • socket_clearance (float, optional) – gap around screw with no recess (e.g. hex head) which allows a socket wrench to be inserted. Defaults to 6mm.

Raises
  • ValueError – invalid size, must be formatted as size-pitch or size-TPI

  • ValueError – invalid fastener_type

  • ValueError – invalid hand, must be one of ‘left’ or ‘right’

  • ValueError – invalid size

Each screw instance creates a set of properties that provide the Solid CAD object as well as valuable parameters, as follows (values intended for internal use are not shown):

Variables
  • tap_drill_sizes (dict) – dictionary of drill sizes for tapped holes

  • tap_hole_diameters (dict) – dictionary of drill diameters for tapped holes

  • clearance_drill_sizes (dict) – dictionary of drill sizes for clearance holes

  • clearance_hole_diameters (dict) – dictionary of drill diameters for clearance holes

  • nominal_lengths (list[float]) – list of nominal screw lengths

  • info (str) – identifying information

  • screw_class (class) – the derived class that created this screw

  • head_height (float) – maximum height of the screw head

  • head_diameter (float) – maximum diameter of the screw head

  • head (Solid) – cadquery Solid screw head as defined by class attributes

The following method helps with hole creation:

Screw.min_hole_depth(counter_sunk=True)

Minimum depth of a hole able to accept the screw

Return type

float

Screw Selection

As there are many classes and types of screws to select from, the Screw class provides some methods that can help find the correct screw for your application. As a reminder, to find the subclasses of the Screw class, use __subclasses__():

>>> Screw.__subclasses__()
[<class 'cq_warehouse.fastener.ButtonHeadScrew'>, ...]

Here is a summary of the class methods:

classmethod Screw.types()

Return a set of the screw types

Return type

List[str]

>>> CounterSunkScrew.types()
{'iso14582', 'iso10642', 'iso14581', 'iso2009', 'iso7046'}
classmethod Screw.sizes(fastener_type)

Return a list of the screw sizes for the given type

Return type

List[str]

>>> CounterSunkScrew.sizes("iso7046")
['M1.6-0.35', 'M2-0.4', 'M2.5-0.45', 'M3-0.5', 'M3.5-0.6', 'M4-0.7', 'M5-0.8', 'M6-1', 'M8-1.25', 'M10-1.5']
classmethod Screw.select_by_size(size)

Return a dictionary of list of fastener types of this size

Return type

dict

  • select_by_size(size:str) : (dict{class:[type,…],} - e.g.:

>>> Screw.select_by_size("M6-1")
{<class 'cq_warehouse.fastener.ButtonHeadScrew'>: ['iso7380_1'], <class 'cq_warehouse.fastener.ButtonHeadWithCollarScrew'>: ['iso7380_2'], ...}

To see if a given screw type has screws in the length you are looking for, each screw class provides a dictionary of available lengths, as follows:

>>> CounterSunkScrew.nominal_length_range["iso7046"]
[3.0, 4.0, 5.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0, 20.0, 25.0, 30.0, 35.0, 40.0, 45.0, 50.0, 55.0, 60.0]

During instantiation of a screw any value of length may be used; however, only a subset of the above nominal_length_range is valid for any given screw size. The valid sub-range is given with the nominal_lengths property as follows:

>>> screw = CounterSunkScrew(fastener_type="iso7046",size="M6-1",length=12 * MM)
>>> screw.nominal_lengths
[8.0, 10.0, 12.0, 14.0, 16.0, 20.0, 25.0, 30.0, 35.0, 40.0, 45.0, 50.0, 55.0, 60.0]

Derived Screw Classes

The following is a list of the current screw classes derived from the base Screw class. Also listed is the type for each of these derived classes where the type refers to a standard that defines the screw parameters. All derived screws inherit the same API as the base Screw class.

  • ButtonHeadScrew: iso7380_1

  • ButtonHeadWithCollarScrew: iso7380_2

  • CheeseHeadScrew: iso14580, iso7048, iso1207

  • CounterSunkScrew: iso2009, iso14582, iso14581, iso10642, iso7046

  • HexHeadScrew: iso4017, din931, iso4014

  • HexHeadWithFlangeScrew: din1662, din1665

  • PanHeadScrew: asme_b_18.6.3, iso1580, iso14583

  • PanHeadWithCollarScrew: din967

  • RaisedCheeseHeadScrew: iso7045

  • RaisedCounterSunkOvalHeadScrew: iso2010, iso7047, iso14584

  • SetScrew: iso4026

  • SocketHeadCapScrew: iso4762, asme_b18.3

Detailed information about any of the screw types can be readily found on the internet from manufacture’s websites or from the standard document itself.

Washer

As the base class of all other washer and bolt classes, all of the derived washer classes share the same interface as follows:

class Washer(size, fastener_type)

Parametric Washer

Base class used to create standard washers

Parameters
  • size (str) – standard sizes - e.g. “M6”

  • fastener_type (str) – type identifier - e.g. “iso4032”

Raises
  • ValueError – invalid fastener_type

  • ValueError – invalid size

Each washer instance creates a set of properties that provide the Solid CAD object as well as valuable parameters, as follows (values intended for internal use are not shown):

Variables
  • clearance_drill_sizes (dict) – dictionary of drill sizes for clearance holes

  • clearance_hole_diameters (dict) – dictionary of drill diameters for clearance holes

  • nominal_lengths (list[float]) – list of nominal screw lengths

  • info (str) – identifying information

  • washer_class (str) – display friendly class name

  • washer_diameter (float) – maximum diameter of the washer

  • washer_thickness (float) – maximum thickness of the washer

Washer Selection

As there are many classes and types of washers to select from, the Washer class provides some methods that can help find the correct washer for your application. As a reminder, to find the subclasses of the Washer class, use __subclasses__():

>>> Washer.__subclasses__()
[<class 'cq_warehouse.fastener.PlainWasher'>, <class 'cq_warehouse.fastener.ChamferedWasher'>, <class 'cq_warehouse.fastener.CheeseHeadWasher'>]

Here is a summary of the class methods:

classmethod Washer.types()

Return a set of the washer types

Return type

List[str]

>>> PlainWasher.types()
{'iso7091', 'iso7089', 'iso7093', 'iso7094'}
classmethod Washer.sizes(fastener_type)

Return a list of the washer sizes for the given type

Return type

List[str]

>>> PlainWasher.sizes("iso7091")
['M1.6', 'M1.7', 'M2', 'M2.3', 'M2.5', 'M2.6', 'M3', 'M3.5', 'M4', 'M5', 'M6', 'M7', 'M8', 'M10', 'M12', 'M14', 'M16', 'M18', 'M20', 'M22', 'M24', 'M26', 'M27', 'M28', 'M30', 'M32', 'M33', 'M35', 'M36']
classmethod Washer.select_by_size(size)

Return a dictionary of list of fastener types of this size

Return type

dict

>>> Washer.select_by_size("M6")
{<class 'cq_warehouse.fastener.PlainWasher'>: ['iso7094', 'iso7093', 'iso7089', 'iso7091'], <class 'cq_warehouse.fastener.ChamferedWasher'>: ['iso7090'], <class 'cq_warehouse.fastener.CheeseHeadWasher'>: ['iso7092']}

Derived Washer Classes

The following is a list of the current washer classes derived from the base Washer class. Also listed is the type for each of these derived classes where the type refers to a standard that defines the washer parameters. All derived washers inherit the same API as the base Washer class.

  • PlainWasher: iso7094, iso7093, iso7089, iso7091

  • ChamferedWasher: iso7090

  • CheeseHeadWasher: iso7092

Detailed information about any of the washer types can be readily found on the internet from manufacture’s websites or from the standard document itself.

Custom Holes

When designing parts with CadQuery a common operation is to place holes appropriate to a specific fastener into the part. This operation is optimized with cq_warehouse by the following three new Workplane methods:

The API for the first three methods are very similar. The fit parameter is used for clearance hole dimensions and to calculate the gap around the head of a countersunk screw. The material parameter controls the size of the tap hole as they differ as a function of the material the part is made of. For clearance and tap holes, depth values of None are treated as thru holes. The threaded hole method requires that depth be specified as a consequence of how the thread is constructed.

These methods use data provided by a fastener instance (either a Nut or a Screw) to both create the appropriate hole (possibly countersunk) in your part as well as add the fastener to a CadQuery Assembly in the location of the hole. In addition, a list of washers can be provided which will get placed under the head of the screw or nut in the provided Assembly.

For example, let’s re-build the parametric bearing pillow block found in the CadQuery Quickstart:

import cadquery as cq
from cq_warehouse.fastener import SocketHeadCapScrew
from cq_warehouse.bearing import SingleRowDeepGrooveBallBearing
import cq_warehouse.extensions

height = 60.0
width = 80.0
thickness = 10.0
padding = 12.0

# make the bearing
bearing = SingleRowDeepGrooveBallBearing(size="M8-22-7", bearing_type="SKT")
# make the screw
screw = SocketHeadCapScrew(
    size="M2-0.4", fastener_type="iso4762", length=16, simple=False
)
# make the assembly
pillow_block = cq.Assembly(None, name="pillow_block")
# make the base
base = (
    cq.Workplane("XY")
    .box(height, width, thickness)
    .faces(">Z")
    .workplane()
    .pressFitHole(bearing=bearing, baseAssembly=pillow_block)
    .faces(">Z")
    .workplane()
    .rect(height - padding, width - padding, forConstruction=True)
    .vertices()
    .clearanceHole(fastener=screw, baseAssembly=pillow_block)
    .edges("|Z")
    .fillet(2.0)
)
pillow_block.add(base, name="base", color=cq.Color(162 / 255, 138 / 255, 255 / 255))
print(pillow_block.fastenerQuantities())

# Render the assembly
if "show_object" in locals():
    show_object(pillow_block)

Which results in:

pillow_block

The differences between this code and the Read the Docs version are:

  • screw and bearing dimensions aren’t required

  • the bearing is created during instantiation of the SingleRowDeepGrooveBallBearing class

  • the screw is created during instantiation of the SocketHeadCapScrew class

  • an assembly is created and later the base is added to that assembly

  • the call to cskHole is replaced with clearanceHole

Not only were the appropriate holes for the bearing and M2-0.4 screws created but an assembly was created to store all of the parts in this project all without having to research the dimensions of the parts.

Note: In this example the simple=False parameter creates accurate threads on each of the screws which significantly increases the complexity of the model. The default of simple is True which models the thread as a simple cylinder which is sufficient for most applications without the performance cost of accurate threads. Also note that the default color of the pillow block “base” was changed to better contrast the screws.

The data used in the creation of these holes is available via three instance methods:

>>> screw = CounterSunkScrew(fastener_type="iso7046", size="M6-1", length=10)
>>> screw.clearance_hole_diameters
{'Close': 6.4, 'Normal': 6.6, 'Loose': 7.0}

>>> screw.clearance_drill_sizes
{'Close': '6.4', 'Normal': '6.6', 'Loose': '7'}
>>> screw.tap_hole_diameters
{'Soft': 5.0, 'Hard': 5.4}
>>> screw.tap_drill_sizes
{'Soft': '5', 'Hard': '5.4'}

Note that with imperial sized holes (e.g. 7/16), the drill sizes could be a fractional size (e.g. 25/64) or a numbered or lettered size (e.g. U). This information can be added to your designs with the drafting sub-package.

Screw and Hole Alignment

If a depth parameter is provided to the hole methods when placing a screw, the screw will be located in the provided assembly such that the tip of the screw to bottom out in the hole. This may result in the screw extending above the top surface. For example:

"""
Example of placing a 20mm long screw into a 10mm deep hole
"""
import cadquery as cq
from cq_warehouse.fastener import SocketHeadCapScrew
import cq_warehouse.extensions

MM = 1
screw_alignment_assembly = cq.Assembly()
capscrew = SocketHeadCapScrew(
    size="M6-1", length=20 * MM, fastener_type="iso4762", simple=False
)
plate = (
    cq.Workplane("XY")
    .box(50 * MM, 50 * MM, 20 * MM)
    .faces(">Z")
    .clearanceHole(
        fastener=capscrew,
        depth=10 * MM,
        counterSunk=False,
        baseAssembly=screw_alignment_assembly,
    )
)
screw_alignment_assembly.add(plate, color=cq.Color("darkseagreen"))

if "show_object" in locals():
    show_object(screw_alignment_assembly, name="screw_alignment_assembly")

Which results in:

screw_at_depth

If no depth is provided, the hole will extend through the part and the screw will be aligned with the surface.

Captive Nuts

The clearanceHole() method has a captiveNut parameter that when used with a hex or square nut will create a hole that captures the nut such that it can’t spin. Here is an example:

import cadquery as cq
from cq_warehouse.fastener import HexNut, SquareNut
import cq_warehouse.extensions

hex_nut = HexNut(size="M6-1", fastener_type="iso4033")
square_nut = SquareNut(size="M6-1", fastener_type="din557")
test_assembly = cq.Assembly()
block = (
    cq.Workplane("XY")
    .box(50, 50, 10)
    .faces(">Z")
    .workplane()
    .pushPoints([(-12.5, 0)])
    .clearanceHole(
        fastener=hex_nut, fit="Loose", captiveNut=True, baseAssembly=test_assembly
    )
    .pushPoints([(+12.5, 0)])
    .clearanceHole(fastener=square_nut, captiveNut=True, baseAssembly=test_assembly)
)
test_assembly.add(block, color=cq.Color("tan"))

if "show_object" in locals():
    show_object(test_assembly, name="test_assembly")

Which results in:

captive_nuts

The space around the nuts is controlled by the fit parameter.

Insert Nuts

The insertHole() is much like the previous three custom hole methods but creates holes for heat set inserts in plastic parts - commonly used in 3D printing.

Workplane.insertHole(fastener, fit='Normal', depth=None, baseAssembly=None, clean=True, manufacturingCompensation=0.0)

Insert Hole

Put a hole appropriate for an insert nut at the provided location

For more information on how to use insertHole() see Custom Holes.

Parameters
  • fastener (Nut) – An insert nut instance

  • fit (Optional[Literal[‘Close’, ‘Normal’, ‘Loose’]]) – one of “Close”, “Normal”, “Loose” which determines clearance hole diameter. Defaults to “Normal”.

  • depth (Optional[float]) – hole depth. Defaults to through part.

  • baseAssembly (Optional[Assembly]) – Assembly to add faster to. Defaults to None.

  • clean (Optional[bool]) – execute a clean operation remove extraneous internal features. Defaults to True.

  • manufacturingCompensation (float, optional) – used to compensate for over-extrusion of 3D printers. A value of 0.2mm will reduce the radius of an external thread by 0.2mm (and increase the radius of an internal thread) such that the resulting 3D printed part matches the target dimensions. Defaults to 0.0.

Raises

ValueError – insertHole only accepts fasteners of type HeatSetNut

Return type

T

Returns

the shape on the workplane stack with a new clearance hole

Fastener Locations

There are two methods that assist with the location of fastener holes relative to other parts: fastenerLocations() and pushFastenerLocations().

To aid in the alignment of fasteners fastenerLocations() scans the provided Assembly for the given fastener and pushes the Location values onto the CadQuery Workplane stack. This method provides offset and flip options which modify the existing fastener locations to allow for a fastener to be positioned on the back side of a structure as shown in the bolt_plates_together.py example.

"""

Bolt Plates Together Example

name: bolt_plates_together.py
by:   Gumyr
date: March 7th 2022

desc: Example of using pushFastenerLocations to align fasteners.

license:

    Copyright 2022 Gumyr

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.

"""
import cadquery as cq
from cq_warehouse.fastener import HexHeadScrew, PlainWasher, HexNutWithFlange
import cq_warehouse.extensions

MM = 1

# Create the fasteners used in this example
hex_bolt = HexHeadScrew(
    size="M6-1", length=20 * MM, fastener_type="iso4014", simple=False
)
flanged_nut = HexNutWithFlange(size="M6-1", fastener_type="din1665")
large_washer = PlainWasher(size="M6", fastener_type="iso7093")

# Create an empty Assembly to hold all of the fasteners
fastener_assembly = cq.Assembly(None, name="top")

# Create the top and bottom plates with holes
top_plate_size = (50 * MM, 100 * MM, 5 * MM)
bottom_plate_size = (100 * MM, 50 * MM, 5 * MM)
top_plate = (
    cq.Workplane("XY", origin=(0, 0, bottom_plate_size[2]))
    .box(*top_plate_size, centered=(True, True, False))
    .faces(">Z")
    .workplane()
    .clearanceHole(
        fastener=hex_bolt,
        washers=[large_washer],
        baseAssembly=fastener_assembly,
        counterSunk=False,
    )
)
bottom_plate = (
    cq.Workplane("XY")
    .box(*bottom_plate_size, centered=(True, True, False))
    .pushFastenerLocations(
        fastener=large_washer,
        baseAssembly=fastener_assembly,
        offset=-(top_plate_size[2] + bottom_plate_size[2]),
        flip=True,
    )
    .clearanceHole(
        fastener=flanged_nut,
        baseAssembly=fastener_assembly,
        counterSunk=False,
    )
)
print(fastener_assembly.fastenerQuantities())

if "show_object" in locals():
    show_object(
        top_plate, name="top_plate", options={"alpha": 0.8, "color": (170, 0, 255)}
    )
    show_object(
        bottom_plate, name="bottom_plate", options={"alpha": 0.8, "color": (255, 85, 0)}
    )
    show_object(fastener_assembly, name="fastener_assembly")
{'HexNutWithFlange(din1665): M6-1': 1, 'HexHeadScrew(iso4014): M6-1x20': 1, 'PlainWasher(iso7093): M6': 1}
_images/bolting_plates.png

In more complex situations many Assemblies may be nested together. To cope with this, the Locations are relative to the Assembly provided and shown in the align_fastener_holes.py example.

"""

Align Fastener Holes Example

name: align_fastener_holes.py
by:   Gumyr
date: December 11th 2021

desc: Example of using the pushFastenerLocations() method to align cq_warehouse.fastener
      holes between to plates in an assembly.

license:

    Copyright 2021 Gumyr

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.

"""
import cadquery as cq
from cq_warehouse.fastener import SocketHeadCapScrew
import cq_warehouse.extensions

# Create the screws that will fasten the plates together
cap_screw = SocketHeadCapScrew(
    size="M2-0.4", length=6, fastener_type="iso4762", simple=False
)

# Two assemblies are required - the top will contain the screws
bracket_assembly = cq.Assembly(None, name="top_plate_assembly")
square_tube_assembly = cq.Assembly(None, name="base_plate_assembly")

# --- Angle Bracket ---

# Create an angle bracket and add clearance holes for the screws
angle_bracket = (
    cq.Workplane("YZ")
    .moveTo(-9, 1)
    .hLine(10)
    .vLine(-10)
    .offset2D(1)
    .extrude(10, both=True)
    .faces(">Z")
    .workplane()
    .pushPoints([(5, -5), (-5, -5)])
    .clearanceHole(fastener=cap_screw, counterSunk=False, baseAssembly=bracket_assembly)
    .faces(">Y")
    .workplane()
    .pushPoints([(0, -7)])
    .clearanceHole(fastener=cap_screw, counterSunk=False, baseAssembly=bracket_assembly)
)
# Add the top plate to the top assembly so it can be placed with the screws
bracket_assembly.add(angle_bracket, name="angle_bracket")
# Add the top plate and screws to the base assembly
square_tube_assembly.add(
    bracket_assembly,
    name="top_plate_assembly",
    loc=cq.Location(cq.Vector(20, 10, 10)),
)

# --- Square Tube ---

# Create the square tube
square_tube = (
    cq.Workplane("YZ").rect(18, 18).rect(14, 14).offset2D(1).extrude(30, both=True)
)
# Complete the square tube assembly by adding the square tube
square_tube_assembly.add(square_tube, name="square_tube")
# Add tap holes to the square tube that align with the angle bracket
square_tube = square_tube.pushFastenerLocations(
    cap_screw, square_tube_assembly
).tapHole(fastener=cap_screw, counterSunk=False, depth=10)


# Where are the cap screw holes in the square tube?
for loc in square_tube_assembly.fastenerLocations(cap_screw):
    print(loc)

# How many fasteners are used in the square_tube_assembly and all sub-assemblies
print(square_tube_assembly.fastenerQuantities())

if "show_object" in locals():
    show_object(angle_bracket, name="angle_bracket")
    show_object(square_tube, name="square_tube")
    show_object(square_tube_assembly, name="square_tube_assembly")
((25.0, 5.0, 12.0), (0.0, -0.0, 0.0))
((15.0, 5.0, 12.0), (0.0, -0.0, 0.0))
((20.0, 12.0, 5.0), (1.5707963267948966, -0.0, 3.141592653589793))
{'SocketHeadCapScrew(iso4762): M2-0.4x6': 3}
_images/fastenerLocations.png

Bill of Materials

As previously mentioned, when an assembly is passed into the three hole methods the fasteners referenced are added to the assembly. A new method has been added to the CadQuery Assembly class - fastenerQuantities() - which scans the assembly and returns a dictionary of either:

  • {fastener: count}, or

  • {fastener.info: count}

For example, the values for the previous pillow block example are:

>>> print(pillow_block.fastenerQuantities())
{'SocketHeadCapScrew(iso4762): M2-0.4x16': 4}

>>> print(pillow_block.fastenerQuantities(bom=False))
{<cq_warehouse.fastener.SocketHeadCapScrew object at 0x7f90560f3a90>: 4}

Note that this method scans the given assembly and all its children for fasteners. To limit the scan to just the current Assembly, set the deep=False optional parameter).

Extending the fastener sub-package

The fastener sub-package has been designed to be extended in the following two ways:

Alternate Sizes

As mentioned previously, the data used to guide the creation of fastener objects is derived from .csv files found in the same place as the source code. One can add to the set of standard sized fasteners by inserting appropriate data into the tables. There is a table for each fastener class; an example of the ‘socket_head_cap_parameters.csv’ is below:

Size

iso4762:dk

iso4762:k

asme_b18.3:dk

asme_b18.3:k

asme_b18.3:s

M2-0.4

3.98

2

M2.5-0.45

4.68

2.5

0.096

0.06

0.05

M3-0.5

5.68

3

0.118

0.073

1/16

0.118

0.073

1/16

#0-80

0.14

0.086

5/64

#1-64

#1-72

#2-56

The first row must contain a ‘Size’ and a set of ‘{fastener_type}:{parameter}’ values. The parameters are taken from the ISO standards where ‘k’ represents the head height of a screw head, ‘dk’ is represents the head diameter, etc. Refer to the appropriate document for a complete description. The fastener ‘Size’ field has the format ‘M{thread major diameter}-{thread pitch}’ for metric fasteners or either ‘#{guage}-{TPI}’ or ‘{fractional major diameter}-{TPI}’ for imperial fasteners (TPI refers to Threads Per Inch). All the data for imperial fasteners must be entered as inch dimensions while metric data is in millimeters.

There is also a ‘nominal_screw_lengths.csv’ file that contains a list of all the lengths supported by the standard, as follows:

Screw_Type

Unit

Nominal_Sizes

din931

mm

30,35,40,45,50,55,60,…

The ‘short’ and ‘long’ values from the first table (not shown) control the minimum and maximum values in the nominal length ranges for each screw.

New Fastener Types

The base/derived class structure was designed to allow the creation of new fastener types/classes. For new fastener classes a 2D drawing of one half of the fastener profile is required. If the fastener has a non circular plan (e.g. a hex or a square) a 2D drawing of the plan is required. If the fastener contains a flange and a plan, a 2D profile of the flange is required. If these profiles or plans are present, the base class will use them to build the fastener. The Abstract Base Class technology ensures derived classes can’t be created with missing components.