build123d/examples/playing_cards_algebra.py

210 lines
6.9 KiB
Python

from typing import Tuple, Union, Literal
from build123d import *
class Club(Sketch):
def __init__(
self,
height: float,
align: Union[Align, Tuple[Align, Align]] = None,
):
l0 = Line((0, -188), (76, -188))
b0 = Bezier(l0 @ 1, (61, -185), (33, -173), (17, -81))
b1 = Bezier(b0 @ 1, (49, -128), (146, -145), (167, -67))
b2 = Bezier(b1 @ 1, (187, 9), (94, 52), (32, 18))
b3 = Bezier(b2 @ 1, (92, 57), (113, 188), (0, 188))
club = l0 + b0 + b1 + b2 + b3
club += mirror(club, about=Plane.YZ)
club = make_face(club)
club = scale(club, by=height / club.bounding_box().size.Y)
super().__init__(club.wrapped)
# self._align(align)
class Spade(Sketch):
def __init__(
self,
height: float,
align: Union[Align, Tuple[Align, Align]] = None,
):
b0 = Bezier((0, 198), (6, 190), (41, 127), (112, 61))
b1 = Bezier(b0 @ 1, (242, -72), (114, -168), (11, -105))
b2 = Bezier(b1 @ 1, (31, -174), (42, -179), (53, -198))
l0 = Line(b2 @ 1, (0, -198))
spade = l0 + b0 + b1 + b2
spade += mirror(spade, about=Plane.YZ)
spade = make_face(spade)
spade = scale(spade, by=height / spade.bounding_box().size.Y)
super().__init__(spade.wrapped)
# self._align(align)
class Heart(Sketch):
def __init__(
self,
height: float,
align: Union[Align, Tuple[Align, Align]] = None,
):
b1 = Bezier((0, 146), (20, 169), (67, 198), (97, 198))
b2 = Bezier(b1 @ 1, (125, 198), (151, 186), (168, 167))
b3 = Bezier(b2 @ 1, (197, 133), (194, 88), (158, 31))
b4 = Bezier(b3 @ 1, (126, -13), (94, -48), (62, -95))
b5 = Bezier(b4 @ 1, (40, -128), (0, -198))
heart = b1 + b2 + b3 + b4 + b5
heart += mirror(objects=heart, about=Plane.YZ)
heart = make_face(heart)
heart = scale(heart, by=height / heart.bounding_box().size.Y)
super().__init__(heart.wrapped)
# self._align(align)
class Diamond(Sketch):
def __init__(
self,
height: float,
align: Union[Align, Tuple[Align, Align]] = None,
):
diamond = Bezier((135, 0), (94, 69), (47, 134), (0, 198))
diamond += mirror(diamond, about=Plane.XZ)
diamond += mirror(diamond, about=Plane.YZ)
diamond = make_face(diamond)
diamond = scale(diamond, by=height / diamond.bounding_box().size.Y)
super().__init__(diamond.wrapped)
# self._align(align)
# The inside of the box fits 2.5x3.5" playing card deck with a small gap
pocket_w = 2.5 * IN + 2 * MM
pocket_l = 3.5 * IN + 2 * MM
pocket_t = 0.5 * IN + 2 * MM
wall_t = 3 * MM # Wall thickness
bottom_t = wall_t / 2 # Top and bottom thickness
lid_gap = 0.5 * MM # Spacing between base and lid
lip_t = wall_t / 2 - lid_gap / 2 # Lip thickness
box_plan = RectangleRounded(pocket_w + 2 * wall_t, pocket_l + 2 * wall_t, pocket_w / 15)
box = extrude(box_plan, amount=bottom_t + pocket_t / 2)
base_top = box.faces().sort_by(Axis.Z).last
walls = Plane(base_top) * offset(box_plan, -lip_t)
box += extrude(walls, amount=pocket_t / 2)
top = Plane.XY.offset(wall_t / 2) * offset(box_plan, -wall_t)
box -= extrude(top, amount=pocket_t)
pocket = extrude(box_plan, amount=pocket_t / 2 + bottom_t)
lid_bottom = offset(box_plan, -(wall_t - lip_t))
pocket -= extrude(lid_bottom, amount=pocket_t / 2)
pocket = Pos(0, 0, (wall_t + pocket_t) / 2) * pocket
plane = Plane(pocket.faces().sort_by().last)
suites = Pos(-0.3 * pocket_w, -0.3 * pocket_l) * Heart(pocket_l / 5)
suites += Pos(-0.3 * pocket_w, 0.3 * pocket_l) * Diamond(pocket_l / 5)
suites += Pos(0.3 * pocket_w, -0.3 * pocket_l) * Spade(pocket_l / 5)
suites += Pos(0.3 * pocket_w, 0.3 * pocket_l) * Club(pocket_l / 5)
suites = plane * suites
lid = pocket - extrude(suites, dir=(0, 0, 1), amount=-wall_t)
class PlayingCard(Compound):
"""PlayingCard
A standard playing card modelled as a Face.
Args:
rank (Literal['A', '2' .. '9', 'J', 'Q', 'K']): card rank
suit (Literal['Clubs', 'Spades', 'Hearts', 'Diamonds']): card suit
"""
width = 2.5 * IN
height = 3.5 * IN
suits = {"Clubs": Club, "Spades": Spade, "Hearts": Heart, "Diamonds": Diamond}
ranks = ["A", "2", "3", "4", "5", "6", "7", "8", "9", "J", "Q", "K"]
def __init__(
self,
rank: Literal["A", "2", "3", "4", "5", "6", "7", "8", "9", "J", "Q", "K"],
suit: Literal["Clubs", "Spades", "Hearts", "Diamonds"],
):
w = PlayingCard.width
h = PlayingCard.height
playing_card = Rectangle(w, h, align=Align.MIN)
if "show" in locals():
show(playing_card)
playing_card = fillet(playing_card.vertices(), radius=w / 15)
if "show" in locals():
show(playing_card)
playing_card -= Pos(w / 7, 8 * h / 9) * Text(
txt=rank,
font_size=w / 7,
)
if "show" in locals():
show(playing_card)
playing_card -= Pos(w / 7, 7 * h / 9,) * PlayingCard.suits[
suit
](height=w / 12)
if "show" in locals():
show(playing_card)
playing_card -= (
Pos((6 * w / 7, 1 * h / 9))
* Rot(0, 0, 180)
* Text(txt=rank, font_size=w / 7)
)
if "show" in locals():
show(playing_card)
playing_card -= (
Pos((6 * w / 7, 2 * h / 9))
* Rot(0, 0, 180)
* PlayingCard.suits[suit](height=w / 12)
)
if "show" in locals():
show(playing_card)
rank_int = PlayingCard.ranks.index(rank) + 1
rank_int = rank_int if rank_int < 10 else 1
center_radius = 0 if rank_int == 1 else w / 3.5
suit_rotation = 0 if rank_int == 1 else -90
suit_height = (0.00159 * rank_int**2 - 0.0380 * rank_int + 0.37) * w
playing_card -= (
Pos(w / 2, h / 2)
* Pos(
radius=center_radius,
count=rank_int,
start_angle=90 if rank_int > 1 else 0,
)
* Rot(0, 0, suit_rotation)
* PlayingCard.suits[suit](
height=suit_height,
)
)
super().__init__(playing_card.wrapped)
playing_card = PlayingCard(rank="A", suit="Spades")
if "show_object" in locals():
show_object(playing_card)
# show_object(outer_box_builder.part, "outer")
# show_object(b, name="b", options={"alpha": 0.8})
show_object(box, "box")
show_object(lid, "lid", options={"alpha": 0.7})
# show_object(walls.sketch, "walls")
# show_object(o, "o")
# show_object(half_club.line)
# show_object(spade_outline.line)
# show_object(b0, "b0")
# show_object(b1, "b1")
# show_object(b2, "b2")
# show_object(b3, "b3")
# show_object(b4, "b4")
# show_object(b5, "b5")
# show_object(l0, "l0")
# show_object(l0, "l0")