2024-07-15 22:57:38 -07:00
|
|
|
import cadquery as Cq
|
|
|
|
from dataclasses import dataclass, field
|
2024-07-25 12:35:36 -07:00
|
|
|
from typing import Tuple, Optional, Union, Callable
|
2024-07-16 15:42:39 -07:00
|
|
|
from nhf.build import Model, TargetKind, target
|
2024-07-15 22:57:38 -07:00
|
|
|
import nhf.utils
|
|
|
|
|
|
|
|
def box_with_centre_holes(
|
|
|
|
length: float,
|
|
|
|
width: float,
|
|
|
|
height: float,
|
|
|
|
hole_loc: list[float],
|
|
|
|
hole_diam: float = 6.0,
|
|
|
|
) -> Cq.Workplane:
|
|
|
|
"""
|
|
|
|
Creates a box with holes along the X axis, marked `conn0, conn1, ...`. The
|
|
|
|
box's y axis is centred
|
|
|
|
"""
|
|
|
|
result = (
|
|
|
|
Cq.Workplane('XY')
|
|
|
|
.box(length, width, height, centered=(False, True, False))
|
|
|
|
.faces(">Z")
|
|
|
|
.workplane()
|
|
|
|
)
|
|
|
|
plane = result
|
|
|
|
for i, x in enumerate(hole_loc):
|
|
|
|
result = result.moveTo(x, 0).hole(hole_diam)
|
|
|
|
plane.moveTo(x, 0).tagPlane(f"conn{i}")
|
|
|
|
return result
|
|
|
|
|
|
|
|
@dataclass
|
|
|
|
class Hole:
|
|
|
|
x: float
|
|
|
|
y: float = 0.0
|
|
|
|
diam: Optional[float] = None
|
|
|
|
tag: Optional[str] = None
|
|
|
|
|
2024-07-22 15:02:26 -07:00
|
|
|
@property
|
|
|
|
def rev_tag(self) -> str:
|
|
|
|
assert self.tag is not None
|
|
|
|
return self.tag + "_rev"
|
|
|
|
|
2024-07-15 22:57:38 -07:00
|
|
|
@dataclass
|
2024-07-16 15:42:39 -07:00
|
|
|
class MountingBox(Model):
|
2024-07-15 22:57:38 -07:00
|
|
|
"""
|
|
|
|
Create a box with marked holes
|
|
|
|
"""
|
|
|
|
length: float = 100.0
|
|
|
|
width: float = 60.0
|
|
|
|
thickness: float = 1.0
|
|
|
|
|
|
|
|
# List of (x, y), diam
|
2024-07-19 16:13:33 -07:00
|
|
|
holes: list[Hole] = field(default_factory=lambda: [])
|
2024-07-15 22:57:38 -07:00
|
|
|
hole_diam: Optional[float] = None
|
|
|
|
|
|
|
|
centred: Tuple[bool, bool] = (False, True)
|
|
|
|
|
|
|
|
generate_side_tags: bool = True
|
2024-07-21 18:45:13 -07:00
|
|
|
# Generate tags on the opposite side
|
|
|
|
generate_reverse_tags: bool = False
|
2024-07-15 22:57:38 -07:00
|
|
|
|
2024-07-23 16:49:25 -07:00
|
|
|
centre_bot_top_tags: bool = False
|
2024-07-23 19:13:06 -07:00
|
|
|
centre_left_right_tags: bool = False
|
2024-07-23 16:49:25 -07:00
|
|
|
|
2024-07-16 23:32:23 -07:00
|
|
|
# Determines the position of side tags
|
2024-07-16 17:18:28 -07:00
|
|
|
flip_y: bool = False
|
|
|
|
|
2024-07-25 12:35:36 -07:00
|
|
|
profile_callback: Optional[Callable[[Cq.Sketch], Cq.Sketch]] = None
|
|
|
|
|
2024-07-22 15:02:26 -07:00
|
|
|
def __post_init__(self):
|
|
|
|
for i, hole in enumerate(self.holes):
|
|
|
|
if hole.tag is None:
|
|
|
|
hole.tag = f"conn{i}"
|
|
|
|
|
2024-07-16 15:42:39 -07:00
|
|
|
@target(kind=TargetKind.DXF)
|
2024-07-15 22:57:38 -07:00
|
|
|
def profile(self) -> Cq.Sketch:
|
|
|
|
bx, by = 0, 0
|
|
|
|
if not self.centred[0]:
|
|
|
|
bx = self.length / 2
|
|
|
|
if not self.centred[1]:
|
|
|
|
by = self.width / 2
|
|
|
|
result = (
|
|
|
|
Cq.Sketch()
|
|
|
|
.push([(bx, by)])
|
|
|
|
.rect(self.length, self.width)
|
|
|
|
)
|
|
|
|
for hole in self.holes:
|
|
|
|
diam = hole.diam if hole.diam else self.hole_diam
|
|
|
|
result.push([(hole.x, hole.y)]).circle(diam / 2, mode='s')
|
2024-07-25 12:35:36 -07:00
|
|
|
if self.profile_callback:
|
|
|
|
profile = self.profile_callback(profile)
|
2024-07-15 22:57:38 -07:00
|
|
|
return result
|
|
|
|
|
|
|
|
def generate(self) -> Cq.Workplane:
|
|
|
|
"""
|
|
|
|
Creates box shape with markers
|
|
|
|
"""
|
|
|
|
result = (
|
|
|
|
Cq.Workplane('XY')
|
|
|
|
.placeSketch(self.profile())
|
|
|
|
.extrude(self.thickness)
|
|
|
|
)
|
|
|
|
plane = result.copyWorkplane(Cq.Workplane('XY')).workplane(offset=self.thickness)
|
2024-07-21 18:45:13 -07:00
|
|
|
reverse_plane = result.copyWorkplane(Cq.Workplane('XY'))
|
2024-07-22 15:02:26 -07:00
|
|
|
for hole in self.holes:
|
|
|
|
assert hole.tag
|
|
|
|
plane.moveTo(hole.x, hole.y).tagPlane(hole.tag)
|
2024-07-21 18:45:13 -07:00
|
|
|
if self.generate_reverse_tags:
|
2024-07-22 15:02:26 -07:00
|
|
|
reverse_plane.moveTo(hole.x, hole.y).tagPlane(hole.rev_tag, '-Z')
|
2024-07-15 22:57:38 -07:00
|
|
|
|
|
|
|
if self.generate_side_tags:
|
2024-07-23 19:13:06 -07:00
|
|
|
if self.centre_left_right_tags:
|
|
|
|
result.faces("<Y").workplane(origin=result.edges("<Y and >Z").val().Center()).tagPlane("left")
|
|
|
|
result.faces(">Y").workplane(origin=result.edges(">Y and >Z").val().Center()).tagPlane("right")
|
|
|
|
else:
|
|
|
|
result.faces("<Y").workplane(origin=result.vertices("<X and <Y and >Z").val().Center()).tagPlane("left")
|
|
|
|
result.faces(">Y").workplane(origin=result.vertices("<X and >Y and >Z").val().Center()).tagPlane("right")
|
2024-07-16 17:18:28 -07:00
|
|
|
|
|
|
|
c_y = ">Y" if self.flip_y else "<Y"
|
2024-07-23 16:49:25 -07:00
|
|
|
if self.centre_bot_top_tags:
|
|
|
|
result.faces("<X").workplane(origin=result.edges(f"<X and >Z").val().Center()).tagPlane("bot")
|
|
|
|
result.faces(">X").workplane(origin=result.edges(f">X and >Z").val().Center()).tagPlane("top")
|
|
|
|
else:
|
|
|
|
result.faces("<X").workplane(origin=result.vertices(f"<X and {c_y} and >Z").val().Center()).tagPlane("bot")
|
|
|
|
result.faces(">X").workplane(origin=result.vertices(f">X and {c_y} and >Z").val().Center()).tagPlane("top")
|
2024-07-15 22:57:38 -07:00
|
|
|
result.faces(">Z").tag("dir")
|
|
|
|
return result
|
|
|
|
|
|
|
|
def marked_assembly(self) -> Cq.Assembly:
|
|
|
|
result = (
|
|
|
|
Cq.Assembly()
|
|
|
|
.add(self.generate(), name="box")
|
|
|
|
)
|
2024-07-22 15:02:26 -07:00
|
|
|
for hole in self.holes:
|
|
|
|
result.markPlane(f"box?{hole.tag}")
|
2024-07-21 18:45:13 -07:00
|
|
|
if self.generate_reverse_tags:
|
2024-07-22 15:02:26 -07:00
|
|
|
result.markPlane(f"box?{hole.rev_tag}")
|
2024-07-15 22:57:38 -07:00
|
|
|
if self.generate_side_tags:
|
|
|
|
(
|
|
|
|
result
|
|
|
|
.markPlane("box?left")
|
|
|
|
.markPlane("box?right")
|
|
|
|
.markPlane("box?dir")
|
|
|
|
.markPlane("box?top")
|
|
|
|
.markPlane("box?bot")
|
|
|
|
)
|
|
|
|
return result.solve()
|