gumyr / cq_warehouse

A cadquery parametric part collection

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

custom threads / nuts

MarcWeber opened this issue · comments

I had a use case pencil like thing with protection cap I wanted to do with screws
This is what I came up with with gumyr's help.
And it works :-)
The question is whether / how to integrate such features into
core library only requiring threads no head no nuts etc.

The idea here is to have the workplane with a location and using that (and it's orientation)
to build the screw with some 'gaps' cutting from original cylinder so that it just works.
The external/internal ThreadCuttings thus build a cylinder to be cutted to then add the
threads. However cutting from an object which has a thread added doesn't work well.
Thus the cylinders to be cutted can be applied first, then all threads get added.

So this example illustrates how to move a thread using
fastener.locate and building the cylinder to be cutted from workplane.
Probably there are better ways but it works.

import cadquery as cq
from copy import copy
from cq_warehouse.fastener import IsoThread, SocketHeadCapScrew

def externalThreadCutting(fastener, clearance = None, workplane=None, simple = False, **kwargs):
	"""
	cyl = (cq.Workplane("XY").circle(12/2).extrude(20))
	thread = IsoThread( major_diameter = 12, pitch = 1, length = 10)
	cyl = externalThreadCutting(fastener=thread, clearance = 12)
	"""
	if workplane == None:
	    workplane = cq.Workplane("XY")

	root_radius = fastener.root_radius
	length = fastener.length
	if simple:
		fastener = (workplane.circle(fastener.apex_radius).circle(root_radius).extrude(length))
	else:
		fastener = fastener.locate(workplane.plane.location)
	if clearance == None:
		clearance = 13
# face = thing.faces(faces),
	tube = workplane.circle(clearance).circle(root_radius).extrude(length)
# return thing.faces(faces).workplane().cut(tube).add(fastener)
	return {"cut": tube, "fastener": fastener}

def internalThreadCutting(fastener, simple = False, workplane = None, **kwargs):
	if workplane == None:
	    workplane = cq.Workplane("XY")
	apex_radius = fastener.apex_radius
	root_radius = fastener.root_radius
	length = fastener.length
	if simple:
		fastener = (workplane.circle(apex_radius).circle(root_radius).extrude(length))
	else:
		fastener = fastener.locate(workplane.plane.location)
	tube = workplane.circle(apex_radius).extrude(length)
	return {"cut": tube, "fastener": fastener}


def _applyFasteners(self, *list):
	cut_fasteners = []
	for l in list:
		x = copy(l)
		ff = x.pop("fastenerFunction")
		if "workplane" in x:
			workplane = x.pop("workplane")
		fastener = ff(**x)
		l["fastener"] = fastener
		cut_fasteners.append(externalThreadCutting(**l) if l["fastener"].external else internalThreadCutting(**l))

	thing = self
	for x in cut_fasteners:
		thing = thing - x["cut"]
	for x in cut_fasteners:
		thing = thing.add(x["fastener"])
	return thing

cq.Workplane.applyFasteners = _applyFasteners


height = 50


D = 12
cap_D = 8
cyl = (cq.Workplane("XY").circle(D/2+10).extrude(height))
thread_fit = 0.1 # eg for resin printers

wp_bottom = cq.Workplane("XY")
wp_top     = cq.Workplane("XY", origin = (0,0, height) ).transformed(rotate=cq.Vector(180,0,0))

fasteners = [
	{"major_diameter" : D, "pitch" : 1, "external" : True, "length" : 5, "simple" : False, "workplane": wp_bottom, "fastenerFunction": IsoThread},
	{"major_diameter" : D, "pitch" : 1, "external" : True, "length" : 5, "simple" : False, "workplane": wp_top, "fastenerFunction": IsoThread},
	{"major_diameter" : cap_D + thread_fit, "pitch" : 1, "external" : False, "length" : 5, "simple" : False, "workplane": wp_top, "fastenerFunction": IsoThread}
]

cyl = cyl.applyFasteners(*fasteners)

Here is your objected as I intended cq_warehouse.threads to be used:

import cadquery as cq
from cq_warehouse.fastener import IsoThread
import cq_warehouse.extensions

height, D, cap_D, recess_width = 50, 12, 8, 7
ext = IsoThread(major_diameter=D, pitch=1, length=5)
int = IsoThread(major_diameter=cap_D, pitch=1, length=5, external=False)
outer_radius = ext.min_radius + 10

profile = (
    cq.Sketch()
    .push([(outer_radius / 2, height / 2)])
    .rect(outer_radius, height)
    .push(
        [
            (ext.min_radius + recess_width / 2, ext.length / 2),
            (ext.min_radius + recess_width / 2, height - ext.length / 2),
        ]
    )
    .rect(recess_width, ext.length, mode="s")
    .push([(int.min_radius / 2, height - int.length / 2)])
    .rect(int.min_radius, int.length, mode="s")
    .clean()
)
cyl = (
    cq.Workplane("XZ")
    .placeSketch(profile)
    .revolve()
    .add(
        [
            ext.cq_object,
            ext.cq_object.moved(cq.Location(cq.Vector(0, 0, height - ext.length))),
            int.cq_object.moved(cq.Location(cq.Vector(0, 0, height - int.length))),
        ]
    )
)

show_object(cyl, "cyl")
show_object(profile, "profile")

image

.. shouldn't have used int as a variable name..