Cosplay/nhf/parts/fasteners.py

163 lines
4.2 KiB
Python
Raw Permalink Normal View History

2024-07-19 00:58:10 -07:00
from dataclasses import dataclass
import math
import cadquery as Cq
from typing import Optional
2024-07-19 00:58:10 -07:00
from nhf import Item, Role
import nhf.utils
2024-07-19 14:06:13 -07:00
@dataclass(frozen=True)
class FlatHeadBolt(Item):
diam_head: float
height_head: float
diam_thread: float
height_thread: float
@property
def name(self) -> str:
return f"Bolt M{int(self.diam_thread)} h{int(self.height_thread)}mm"
2024-07-21 05:46:18 -07:00
@property
def role(self) -> Role:
return Role.CONNECTION
2024-07-19 14:06:13 -07:00
def generate(self) -> Cq.Assembly:
head = Cq.Solid.makeCylinder(
radius=self.diam_head / 2,
height=self.height_head,
)
rod = (
Cq.Workplane('XY')
.cylinder(
radius=self.diam_thread/ 2,
height=self.height_thread,
centered=(True, True, False))
)
rod.faces("<Z").tag("tip")
rod.faces(">Z").tag("root")
2024-07-21 05:46:18 -07:00
rod = rod.union(head.located(Cq.Location((0, 0, self.height_thread))))
return rod
2024-07-19 14:06:13 -07:00
2024-07-19 00:58:10 -07:00
@dataclass(frozen=True)
class ThreaddedKnob(Item):
"""
2024-07-19 21:00:10 -07:00
A threaded rod with knob on one side
2024-07-19 00:58:10 -07:00
"""
2024-07-19 14:06:13 -07:00
diam_thread: float
height_thread: float
2024-07-19 00:58:10 -07:00
diam_knob: float
diam_neck: float
height_neck: float
height_knob: float
@property
def name(self) -> str:
2024-07-19 14:06:13 -07:00
return f"Knob M{int(self.diam_thread)} h{int(self.height_thread)}mm"
2024-07-19 00:58:10 -07:00
def generate(self) -> Cq.Assembly:
knob = Cq.Solid.makeCylinder(
radius=self.diam_knob / 2,
height=self.height_knob,
)
neck = Cq.Solid.makeCylinder(
radius=self.diam_neck / 2,
height=self.height_neck,
)
2024-07-19 14:06:13 -07:00
thread = (
2024-07-19 00:58:10 -07:00
Cq.Workplane('XY')
.cylinder(
2024-07-19 14:06:13 -07:00
radius=self.diam_thread / 2,
height=self.height_thread,
2024-07-19 00:58:10 -07:00
centered=(True, True, False))
)
2024-07-19 14:06:13 -07:00
thread.faces("<Z").tag("tip")
thread.faces(">Z").tag("root")
2024-07-19 00:58:10 -07:00
return (
Cq.Assembly()
2024-07-19 14:06:13 -07:00
.addS(thread, name="thread", role=Role.CONNECTION)
2024-07-19 00:58:10 -07:00
.addS(neck, name="neck", role=Role.HANDLE,
2024-07-19 14:06:13 -07:00
loc=Cq.Location((0, 0, self.height_thread)))
2024-07-19 00:58:10 -07:00
.addS(knob, name="knob", role=Role.HANDLE,
2024-07-19 14:06:13 -07:00
loc=Cq.Location((0, 0, self.height_thread + self.height_neck)))
2024-07-19 00:58:10 -07:00
)
@dataclass(frozen=True)
class HexNut(Item):
2024-07-19 14:06:13 -07:00
diam_thread: float
2024-07-19 00:58:10 -07:00
pitch: float
2024-07-19 14:06:13 -07:00
thickness: float
width: float
2024-07-19 00:58:10 -07:00
def __post_init__(self):
2024-07-19 14:06:13 -07:00
assert self.width > self.diam_thread
2024-07-19 00:58:10 -07:00
@property
def name(self):
2024-07-19 14:06:13 -07:00
return f"HexNut M{int(self.diam_thread)}-{self.pitch}"
2024-07-19 00:58:10 -07:00
@property
2024-07-19 21:00:10 -07:00
def role(self) -> Role:
2024-07-19 00:58:10 -07:00
return Role.CONNECTION
@property
def radius(self) -> float:
return self.width / math.sqrt(3)
2024-07-19 00:58:10 -07:00
def generate(self) -> Cq.Workplane:
result = (
Cq.Workplane("XY")
.sketch()
.regularPolygon(r=self.radius, n=6)
2024-07-19 14:06:13 -07:00
.circle(r=self.diam_thread/2, mode='s')
2024-07-19 00:58:10 -07:00
.finalize()
2024-07-19 14:06:13 -07:00
.extrude(self.thickness)
2024-07-19 00:58:10 -07:00
)
result.faces("<Z").tag("bot")
result.faces(">Z").tag("top")
2024-07-19 15:06:57 -07:00
result.copyWorkplane(Cq.Workplane('XY')).tagPlane("dirX", direction="+X")
2024-07-19 00:58:10 -07:00
return result
def cutting_face(self) -> Cq.Face:
return (
Cq.Sketch()
.regularPolygon(r=self.radius, n=6)
._faces
)
@dataclass(frozen=True)
class Washer(Item):
diam_thread: float
diam_outer: float
thickness: float
material_name: Optional[float] = None
def __post_init__(self):
assert self.diam_outer > self.diam_thread
@property
def name(self):
suffix = (" " + self.material_name) if self.material_name else ""
return f"Washer M{int(self.diam_thread)}{suffix}"
@property
def role(self) -> Role:
return Role.CONNECTION
def generate(self) -> Cq.Workplane:
result = (
Cq.Workplane('XY')
.cylinder(
radius=self.diam_outer/2,
height=self.thickness,
)
.faces(">Z")
.hole(self.diam_thread)
)
result.faces("<Z").tag("bot")
result.faces(">Z").tag("top")
return result