Cosplay/nhf/parts/fasteners.py

163 lines
4.2 KiB
Python

from dataclasses import dataclass
import math
import cadquery as Cq
from typing import Optional
from nhf import Item, Role
import nhf.utils
@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"
@property
def role(self) -> Role:
return Role.CONNECTION
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")
rod = rod.union(head.located(Cq.Location((0, 0, self.height_thread))))
return rod
@dataclass(frozen=True)
class ThreaddedKnob(Item):
"""
A threaded rod with knob on one side
"""
diam_thread: float
height_thread: float
diam_knob: float
diam_neck: float
height_neck: float
height_knob: float
@property
def name(self) -> str:
return f"Knob M{int(self.diam_thread)} h{int(self.height_thread)}mm"
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,
)
thread = (
Cq.Workplane('XY')
.cylinder(
radius=self.diam_thread / 2,
height=self.height_thread,
centered=(True, True, False))
)
thread.faces("<Z").tag("tip")
thread.faces(">Z").tag("root")
return (
Cq.Assembly()
.addS(thread, name="thread", role=Role.CONNECTION)
.addS(neck, name="neck", role=Role.HANDLE,
loc=Cq.Location((0, 0, self.height_thread)))
.addS(knob, name="knob", role=Role.HANDLE,
loc=Cq.Location((0, 0, self.height_thread + self.height_neck)))
)
@dataclass(frozen=True)
class HexNut(Item):
diam_thread: float
pitch: float
thickness: float
width: float
def __post_init__(self):
assert self.width > self.diam_thread
@property
def name(self):
return f"HexNut M{int(self.diam_thread)}-{self.pitch}"
@property
def role(self) -> Role:
return Role.CONNECTION
@property
def radius(self) -> float:
return self.width / math.sqrt(3)
def generate(self) -> Cq.Workplane:
result = (
Cq.Workplane("XY")
.sketch()
.regularPolygon(r=self.radius, n=6)
.circle(r=self.diam_thread/2, mode='s')
.finalize()
.extrude(self.thickness)
)
result.faces("<Z").tag("bot")
result.faces(">Z").tag("top")
result.copyWorkplane(Cq.Workplane('XY')).tagPlane("dirX", direction="+X")
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