Cosplay/nhf/parts/fasteners.py

163 lines
4.2 KiB
Python
Raw Permalink Normal View History

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