Catalogue Selected Fastener

In the last section (Exactly Sized Fastener), we used the analysis from the VectorEvaluator to precicely create a WoodScrew with exactly the right dimensions.

The problem with this strategy is that a screw with those exact dimensions is unlikely to exist, and would need to be custom made.

As an alternative, we can change the Selector to pick a screw from a list, more specifically, a Catalogue.

Populate a Catalogue

First we need to create an empty JSONCatalogue.

from cqparts.catalogue import JSONCatalogue
import tempfile

# Temporary catalogue (just for this script)
catalogue_filename = tempfile.mkstemp()[1]
catalogue = JSONCatalogue(catalogue_filename)

Now we’ll add some screws to the catalogue:

# Add screws to catalogue
#   note: this is the kind of information you'd store in a csv
#   file, then import with a script similar to this one, to convert that
#   information to a Catalogue.
screws = [
    {
        'id': 'screw_30',
        'obj_params': {  # parameters to WoodScrew constructor
            'neck_exposed': 5,
            'length': 40,  # exposing 10mm of thread
            'neck_length': 30,
        },
        'criteria': {
            'type': 'screw',
            'thread_length': 10,
            'compatible_anchor': 'anchor_10',
        },
    },
    {
        'id': 'screw_50',
        'obj_params': {
            'neck_exposed': 6,
            'length': 65,  # exposing 15mm of thread
            'neck_length': 50,

        },
        'criteria': {
            'type': 'screw',
            'thread_length': 15,
            'compatible_anchor': 'anchor_15',
        },
    },
]
for screw in screws:
    obj = WoodScrew(**screw['obj_params'])
    catalogue.add(id=screw['id'], criteria=screw['criteria'], obj=obj)

And some anchors:

# Add anchors to catalogue
anchors = [
    {
        'id': 'anchor_10',
        'obj_params': {  # parameters to WoodScrew constructor
            'diameter': 10,
            'height': 7,
        },
        'criteria': {'type': 'anchor'},
    },
    {
        'id': 'anchor_15',
        'obj_params': {  # parameters to WoodScrew constructor
            'diameter': 15,
            'height': 10,
        },
        'criteria': {'type': 'anchor'},
    },
]
for anchor in anchors:
    obj = Anchor(**anchor['obj_params'])
    catalogue.add(id=anchor['id'], criteria=anchor['criteria'], obj=obj)

Then close() the catalogue to commit the items we’ve added to file.

To learn more about catalogues, read: Catalogue.

Catalogue Selector

Our previous implementation of the Fastener in Exactly Sized Fastener can mostly be salvaged, so we’ll inherit from it, but just change how the screw and anchor components are selected and instantiated (in the overridden get_components() method).

from cqparts.catalogue import JSONCatalogue
from cqparts.utils import property_buffered

class EasyInstallCatalogueFastener(EasyInstallFastener):

    class Selector(EasyInstallFastener.Selector):

        def get_components(self):
            # Find minimum neck length (total effect length, minus last effect)
            neck_length_min = abs(self.evaluator.eval[-1].start_point - self.evaluator.eval[0].start_point)
            thread_length_max = abs(self.evaluator.eval[-1].end_point - self.evaluator.eval[-1].start_point)

            # Get the catalogue of available items
            catalogue = JSONCatalogue(catalogue_filename)
            item = catalogue.get_query()

            # Find viably sized wood-screw
            screw_item = sorted(
                catalogue.search(
                    # eval sets minimum evaluation length
                    (item.obj.params.neck_length >= neck_length_min) &
                    # thread shouldn't pierce through last part
                    (item.criteria.thread_length < thread_length_max)
                ),
                # sort by shortest first
                key=lambda x: x['obj']['params']['neck_length']
            )[0]  # first result; shortest screw

            return {
                'screw': catalogue.deserialize_item(screw_item),
                'anchor': catalogue.get(
                    item.id == screw_item['criteria']['compatible_anchor']
                ),
            }

Result

Now, to re-use everything else we’ve done, we can inherit from ConnectedPlanks but use a different fastener class:

class ConnectedPlanksCatalogue(ConnectedPlanks):
    fastener_class = EasyInstallCatalogueFastener