Compare commits

..

2 Commits

Author SHA1 Message Date
noah metz e29ac003e4 Added pack files 2025-05-26 12:09:44 -06:00
noah metz b3d8983d41 Changed to class-based, and changing style of holder 2025-05-26 12:07:26 -06:00
3 changed files with 229 additions and 85 deletions

@ -2,9 +2,6 @@ from ocp_vscode import *
from build123d import *
from math import tan, sin, cos, sqrt, pi, radians
def PG(x, y, angle):
return Polygon((x, 0), (x, y), (0, y + x * tan(radians(angle))), (0, x * tan(radians(angle))), align=Align.MIN)
def CounterVal(val):
if val >= 0:
return f"+{val}"
@ -17,81 +14,121 @@ def NumStr(val):
else:
return f"{val}"
def Chevron(x, y, angle):
return PG(x/2, y, angle) + mirror(PG(x/2, y, angle), Plane.YZ)
def Numerical(left, right, x, y, t, angle, base_z, text_z):
base = Chevron(x, y, angle)
counter = extrude(base, base_z + text_z, (0, 0, 1))
cutout = Pos(t/2, t + t*tan(radians(angle)), 0) * PG(x/2 - 1.5*t, y - 2*t, angle)
cutout += mirror(cutout, Plane.YZ)
cutout = Pos(0, 0, base_z + text_z) * cutout
counter -= extrude(cutout, text_z, (0, 0, -1))
font_size = x/5
font = "Arial Rounded MT Bold"
top = Pos(-x/4, y/2 + (t/2+x/4)*tan(radians(angle))) * Rot(0, 0, angle) * Text(CounterVal(left), font_size, font=font)
top += Pos(x/4, y/2 + (t/2+x/4)*tan(radians(angle))) * Rot(0, 0, -angle) * Text(CounterVal(right), font_size, font=font)
counter += extrude(Pos(0, 0, base_z) * top, text_z, (0, 0, 1))
return counter
def Textual(text, image, image_yoff, font_mul, x, y, t, angle, base_z, text_z):
counter = extrude(Chevron(x, y, angle), base_z + text_z, (0, 0, 1))
counter -= extrude(Pos(0, t + t*tan(radians(angle)), base_z + text_z) * Chevron(x-2*t, y-2*t, angle), text_z, (0, 0, -1))
font = "Arial Rounded MT Bold"
font_size = x/6 * font_mul
top = Pos(0, x/2*tan(radians(angle)) + t + 2*font_size/3) * Text(text, font_size, font=font, align=(Align.CENTER, Align.NONE))
svg = import_svg(image, align=(Align.CENTER, Align.MIN))
svg_max_x = max([x.bounding_box().max.X for x in svg.faces()])
svg_max_y = max([x.bounding_box().max.Y for x in svg.faces()])
svg_min_x = min([x.bounding_box().min.X for x in svg.faces()])
svg_min_y = min([x.bounding_box().min.Y for x in svg.faces()])
svg_size = (svg_max_x - svg_min_x, svg_max_y - svg_min_y)
svg_scale = min(x/svg_size[0] * 0.6, y/svg_size[1] * 0.6)
svg_obj = [Pos(0, y + t + image_yoff*svg_scale - svg_size[1]*svg_scale) * face for face in svg.faces()]
top += scale(svg_obj, (svg_scale, svg_scale, svg_scale))
counter += extrude(Pos(0, 0, base_z) * top, text_z, (0, 0, 1))
return counter
x = 25 * MM
y = 10 * MM
t = 1 * MM
base_z = 1 * MM
text_z = 0.5 * MM
angle = 22.5
def Holder(x, y, angle):
fit = 1 * MM
spacing = 3 * MM
total_x = x + fit
cut_size = 85 * MM
top_cut = 5 * MM
max_depth = 30 * MM
part = import_step("multibin_insert_2_2_1.step")
insert_top = part.faces().sort_by(SortBy.AREA)[-1]
part -= extrude(insert_top, top_cut, (0, 0, -1))
cut_profile = Location((0, -cut_size/2, max_depth), (-90, 0, 0)) * Pos(0, -total_x/2*tan(radians(angle))) * Chevron(total_x, y + max_depth, angle)
cut_profile = Plane(insert_top) * cut_profile
cuts = [(10, 2.5*y), (3, y), (1, y)]
for j, (num_cuts, depth) in enumerate(cuts):
cut_len = (cut_size-(num_cuts-1)*spacing)/num_cuts
for i in range(num_cuts):
part -= Pos((-1+j)*(total_x + spacing), i*cut_len + i*spacing, -depth) * extrude(cut_profile, cut_len)
return part
class Parallelogram(BaseSketchObject):
def __init__(
self,
width: float,
height: float,
angle: float,
rotation: float = 0,
align: tuple[Align, Align] = (Align.CENTER, Align.CENTER),
mode: Mode = Mode.ADD,
):
obj = Polygon((width, 0), (width, height), (0, height + width * tan(radians(angle))), (0, width * tan(radians(angle))), align=Align.MIN)
super().__init__(obj=obj, rotation=rotation, align=align, mode=mode)
class Chevron(BaseSketchObject):
def __init__(
self,
width: float,
height: float,
angle: float = 22.5,
rotation: float = 0,
align: tuple[Align, Align] = (Align.CENTER, Align.CENTER),
mode: Mode = Mode.ADD,
):
chevron = Parallelogram(width/2, height, angle, align=(Align.MIN, Align.MIN))
chevron += mirror(chevron, Plane.YZ)
super().__init__(obj=chevron, rotation=rotation, align=align, mode=mode)
class NumericalCounter(BasePartObject):
def __init__(
self,
left: str,
right: str,
width: float = 25 * MM,
height: float = 10 * MM,
wall: float = 1 * MM,
base_z: float = 1 * MM,
text_z: float = 0.5 * MM,
angle: float = 22.5,
rotation: VectorLike = (0, 0, 0),
align: tuple[Align, Align] = (Align.CENTER, Align.CENTER),
mode: Mode = Mode.ADD,
):
base = Chevron(width, height, angle, align=(Align.CENTER, Align.MIN))
part = extrude(base, base_z + text_z, (0, 0, 1))
cutout = Pos(wall/2, wall + wall*tan(radians(angle)), 0) * Parallelogram(width/2 - 1.5*wall, height - 2*wall, angle, align=(Align.MIN, Align.MIN))
cutout += mirror(cutout, Plane.YZ)
cutout = Pos(0, 0, base_z + text_z) * cutout
part -= extrude(cutout, text_z, (0, 0, -1))
font_size = width/5
font = "Arial Rounded MT Bold"
top = Pos(-width/4, height/2 + (wall/2+width/4)*tan(radians(angle))) * Rot(0, 0, angle) * Text(CounterVal(left), font_size, font=font)
top += Pos(width/4, height/2 + (wall/2+width/4)*tan(radians(angle))) * Rot(0, 0, -angle) * Text(CounterVal(right), font_size, font=font)
part += extrude(Pos(0, 0, base_z) * top, text_z, (0, 0, 1))
super().__init__(part=part, rotation=rotation, align=align, mode=mode)
class SVGCounter(BasePartObject):
def __init__(
self,
text: str,
image: str,
image_yoff: float = 0,
font_mul: float = 1,
font: str = "Arial Rounded MT Bold",
width: float = 25 * MM,
height: float = 25 * MM,
angle: float = 22.5,
wall: float = 1 * MM,
base_z: float = 1 * MM,
text_z: float = 0.5 * MM,
rotation: VectorLike = (0, 0, 0),
align: tuple[Align, Align] = (Align.CENTER, Align.CENTER),
mode: Mode = Mode.ADD,
):
part = extrude(Chevron(width, height, angle, align=(Align.CENTER, Align.MIN)), base_z + text_z, (0, 0, 1))
part -= extrude(Pos(0, wall + wall*tan(radians(angle)), base_z + text_z) * Chevron(width-2*wall, height-2*wall, angle, align=(Align.CENTER, Align.MIN)), text_z, (0, 0, -1))
font_size = width/6 * font_mul
top = Pos(0, width/2*tan(radians(angle)) + wall + 2*font_size/3) * Text(text, font_size, font=font, align=(Align.CENTER, Align.NONE))
svg = import_svg(image, align=(Align.CENTER, Align.MIN))
svg_max_x = max([x.bounding_box().max.X for x in svg.faces()])
svg_max_y = max([x.bounding_box().max.Y for x in svg.faces()])
svg_min_x = min([x.bounding_box().min.X for x in svg.faces()])
svg_min_y = min([x.bounding_box().min.Y for x in svg.faces()])
svg_size = (svg_max_x - svg_min_x, svg_max_y - svg_min_y)
svg_scale = min(width/svg_size[0] * 0.6, height/svg_size[1] * 0.6)
svg_obj = [Pos(0, height + wall + image_yoff*svg_scale - svg_size[1]*svg_scale) * face for face in svg.faces()]
top += scale(svg_obj, (svg_scale, svg_scale, svg_scale))
part += extrude(Pos(0, 0, base_z) * top, text_z, (0, 0, 1))
super().__init__(part=part, rotation=rotation, align=align, mode=mode)
class Holder(BasePartObject):
def __init__(
self,
rotation: VectorLike = (0, 0, 0),
align: tuple[Align, Align] = (Align.CENTER, Align.CENTER),
mode: Mode = Mode.ADD,
):
cut = 5 * MM
max_depth = 36 * MM - cut
depth = max_depth
part = import_step("multibin_insert_2_2_1.step")
insert_top = part.faces().sort_by(SortBy.AREA)[-1]
part -= extrude(Plane(insert_top) * (Pos(0, 0, 2*MM) * Rectangle(500*MM, 500*MM)), cut, (0, 0, -1))
part -= extrude(insert_top, cut + depth, (0, 0, -1))
super().__init__(part=part, rotation=rotation, align=align, mode=mode)
number_counters = [
(1, 1),
@ -130,12 +167,15 @@ image_counters = {
"Charge": (0, 1),
}
xoff = 25 * MM
yoff = 25 * MM
export = True
one = False
one = True
display_objects = []
for idx, numbers in enumerate(number_counters):
counter = Numerical(numbers[0], numbers[1], x, y, t, angle, base_z, text_z)
display_objects.append(Pos((x + 1 * MM) * idx, 0) * counter)
counter = NumericalCounter(numbers[0], numbers[1])
display_objects.append(Pos((xoff + 1 * MM) * idx, 0) * counter)
if export:
export_step(counter, f"step/counter_{NumStr(numbers[0])}_{NumStr(numbers[1])}.step")
export_stl(counter, f"stl/counter_{NumStr(numbers[0])}_{NumStr(numbers[1])}.stl")
@ -143,16 +183,16 @@ for idx, numbers in enumerate(number_counters):
break
for idx, (text, (y_off, font_mul)) in enumerate(image_counters.items()):
counter = Textual(text, f"svg/{text.lower().replace(' ', '')}.svg", y_off, font_mul, x, 2.5*y, t, angle, base_z, text_z)
display_objects.append(Pos((x + 1 * MM) * idx, y + 1 * MM) * counter)
counter = SVGCounter(text, f"svg/{text.lower().replace(' ', '')}.svg", y_off, font_mul)
display_objects.append(Pos((xoff + 1 * MM) * idx, yoff + 1 * MM) * counter)
if export:
export_step(counter, f"step/counter_{text.lower().replace(' ', '')}.step")
export_stl(counter, f"stl/counter_{text.lower().replace(' ', '')}.stl")
if one:
break
holder = Holder(x, y, angle)
display_objects.append(Pos(0, 4*x) * holder)
holder = Holder()
display_objects.append(Pos(0, 4*xoff) * holder)
if export:
export_stl(holder, "holder.stl")
export_step(holder, "holder.step")

@ -0,0 +1,87 @@
from ocp_vscode import *
from build123d import *
from math import sqrt, atan, pi, cos, radians
class HingeProfile(BaseSketchObject):
def __init__(
self,
height: float,
width: float,
thickness: float = 0.5*MM,
radius: float = 0.25 * MM,
rotation: float = 0,
align: tuple[Align, Align] = (Align.CENTER, Align.CENTER),
mode: Mode = Mode.ADD,
):
circle = Pos(0, (thickness/2 + radius)) * Circle(radius)
line_1 = PointArcTangentLine(( width/2, height/2), circle.edge())
line_2 = PointArcTangentLine((-width/2, height/2), circle.edge(), Side.RIGHT)
lines = Curve() + [
Line((width/2, height/2), (width/2, 0)),
line_1,
RadiusArc(line_1.end_point(), line_2.end_point(), radius),
line_2,
Line((-width/2, height/2), (-width/2, 0)),
Line((-width/2, 0), (width/2, 0)),
]
hinge_profile = make_face(lines)
hinge_profile += mirror(hinge_profile)
super().__init__(obj=make_face(hinge_profile), rotation=rotation, align=align, mode=mode)
class Pack(BasePartObject):
def __init__(
self,
card_x: float = 69.5 * MM,
card_y: float = 95 * MM,
card_t: float = (0.3+2*0.04+2*0.12) * MM,
card_num: int = 15,
fit: float = 1 * MM,
wall: float = 2 * MM,
magnet_d: float = 10 * MM,
magnet_t: float = 1 * MM,
hinge_thickness: float = 0.5 * MM,
hinge_radius: float = 0.25 * MM,
rotation: float = (0, 0, 0),
align: tuple[Align, Align] = (Align.CENTER, Align.CENTER),
mode: Mode = Mode.ADD,
):
x = card_x + fit
y = card_y + fit
z = card_t*card_num + fit
s = wall
profile = Rectangle(x, y)
for i in [-1, 1]:
profile += Pos(0, i*(y/2 + s + z/2)) * Rectangle(x, z)
profile += Pos(0, i*(y/2 + y/8 + 2*s + z)) * Rectangle(x, y/4)
profile += Pos(i*(x/2 + s + z/2 + wall/2), 0) * Rectangle(z + wall, y)
profile += Pos(i*(3*x/4 + 2*s + z + wall), 0) * Rectangle(x/2, y)
part = extrude(profile, wall)
hinge = HingeProfile(s, wall, hinge_thickness, hinge_radius, (90, 0, 0), align=(Align.CENTER, Align.MIN))
for i in [-1, 1]:
part += extrude(Pos(i*(x/2+s/2), -y/2) * hinge, y, (0, 1, 0))
part += extrude(Pos(i*(x/2+3*s/2+z+wall), -y/2) * hinge, y, (0, 1, 0))
part += extrude(Pos(-x/2, i*(y/2+s/2)) * Rot(0, 0, 90) * hinge, x, (1, 0, 0))
part += extrude(Pos(-x/2, i*(y/2+3*s/2+z)) * Rot(0, 0, 90) * hinge, x, (1, 0, 0))
magnet_hole = extrude(Pos(0, 0, (wall-magnet_t)/2) * Circle(magnet_d/2), magnet_t)
for i in [-1, 1]:
for j in [-1, 1]:
part -= Pos(j*x/4, i*(y/2 + 2*s + z + y/8)) * magnet_hole
part -= Pos(j*(x/2 + 2*s + wall + z + x/4), i*(y/2 - y/8)) * magnet_hole
super().__init__(part=part, rotation=rotation, align=align, mode=mode)
pack_superthin = Pack(wall=1*MM, hinge_thickness=0.25*MM, hinge_radius=0.2*MM)
export_stl(pack_superthin, "pack_superthin.stl")
show(pack_superthin)
pack = Pack()
export_stl(pack, "pack.stl")
show(pack)

@ -0,0 +1,17 @@
from ocp_vscode import *
from build123d import *
card_x = 69.5 * MM
card_y = 95 * MM
card_z = (0.3+2*0.04+2*0.12) * MM
card_num = 15
fit = 1 * MM
wall = 1 * MM
radius = 2 * MM
profile = RectangleRounded(card_x + fit + 2*wall, card_z * card_num + fit + 2*wall, radius)
cut_profile = Pos(0, 0, wall) * RectangleRounded(card_x + fit, card_z * card_num + fit, radius/2)
pack = extrude(profile, card_y + fit + 1*wall)
pack -= extrude(cut_profile, card_y + fit)
show(pack)
export_stl(pack, "pack.stl")