create a trivial case from the phone model

missing cutouts for ports and extrusions for buttons
This commit is contained in:
2023-12-26 11:53:01 +00:00
parent 4c3600efea
commit aa57d262e2
3 changed files with 72 additions and 12 deletions

View File

@@ -15,10 +15,15 @@ import sys
sys.path.append(os.path.join(os.getcwd(), "src"))
import case
import pinephone
logger = logging.getLogger(__name__)
def model():
phone = pinephone.PinePhone()
return case.case(phone)
def main():
logging.basicConfig()
logging.getLogger().setLevel(logging.INFO)
@@ -28,14 +33,14 @@ def main():
args = parser.parse_args()
model = pinephone.PinePhone()
model_ = model()
if args.export_stl:
logger.info("exporting stl to %s", args.export_stl)
cq.exporters.export(model, args.export_stl)
cq.exporters.export(model_, args.export_stl)
if __name__ == "__main__":
main()
else:
result = pinephone.PinePhone()
result = model()

48
src/case.py Normal file
View File

@@ -0,0 +1,48 @@
# thickness of case walls
thickness = 1.5
# how far into the xy plane to extend the part of the case which covers the front of the phone.
# the top of the phone contains stuff we don't want to cover (camera); the bottom has more margin
overhang_leftright = 2
overhang_top = 2
overhang_bot = 3.5
# how much to smooth the overhangs, else the main opening in front appears rectangular
overhang_radius = 2
def _makeShell(solid, thickness: float=1, combine=False, **kwargs):
"""
dilate the solid in all dimensions by `thickness` and then subtract the original.
this implementation only behaves as expected if the solid has no cusps (i.e. is smoothed).
so for boxes, one should fillet the edges first (even if fillet'd with a trivial radius).
"""
# alternatively, to perform an inset, set thickness negative and use combine="cut"
return solid.faces().each(lambda f: f.thicken(thickness), combine=combine)
def front_cutaway(case):
# split the case into the front part, which covers the screen, and the rear
split_case = case.faces("<<Z").workplane(offset=-thickness).split(keepTop=True, keepBottom=True)
case_front, case_back = split_case.all()
# front_face is a closed face capturing the full width/height of the case at z=0
front_face = case_front.faces(">Z")
# we only want to cutaway this smaller part of the front face
front_face_cutaway = (front_face
.intersect(front_face.translate((thickness + overhang_leftright, 0, 0)))
.intersect(front_face.translate((-(thickness + overhang_leftright), 0, 0)))
.intersect(front_face.translate((0, thickness + overhang_top, 0)))
.intersect(front_face.translate((0, -thickness - overhang_bot, 0)))
)
# "extrude" the front_face_cutaway into something thick enough to actually cut out
# N.B.: i think this logic isn't 100% correct
return front_face_cutaway.faces("|Z").each(lambda f: f.thicken(-thickness), combine=False) \
.edges("|Z").fillet(overhang_radius)
# front_cutaway = front_face_cutaway.faces("|Z").each(lambda f: f.thicken(thickness).translate((0, 0, -thickness)), combine=False)
def case(phone):
# the entire case, before cutting anything away:
case = _makeShell(phone.solids(tag="body"), thickness)
case = case.cut(front_cutaway(case))
# TODO: compress the case along the Z axis, to give a snugger fit (0.8mm is a good compression)
# TODO: cut out the camera, aux, USB.
# TODO: add in volume/power buttons
return case

View File

@@ -52,19 +52,20 @@ def body():
return (
cq.Workplane("front")
.box(body_width, body_length, body_height, centered=False)
.tag("body")
.tag("body_box")
.faces(">>X").tag("body_right")
.faces(tag="body")
.faces(tag="body_box")
.faces("<<Y").tag("body_top")
.faces(tag="body")
.faces(tag="body_box")
.faces(">>Y").tag("body_bottom")
.faces(tag="body")
.faces(tag="body_box")
.faces(">>Z").tag("body_back")
.faces(tag="body")
.faces(tag="body_box")
.edges("|Z")
.fillet(body_rad_xy)
.edges("(|X or |Y)")
.fillet(body_rad_front_z)
.tag("body")
)
def volume(body):
@@ -74,9 +75,12 @@ def volume(body):
.workplane()
.move(volume_min_y, volume_min_z)
.box(volume_length, volume_height, volume_width, centered=False, combine=False)
.tag("volume")
.tag("volume_box")
.edges("|X")
.fillet(volume_height/3) #< XXX: cadquery doesn't allow a full 1/2 fillet
# .edges("|Y")
# .fillet(volume_width/3)
.tag("volume")
)
def power(body):
@@ -86,9 +90,10 @@ def power(body):
.workplane()
.move(power_min_y, power_min_z)
.box(power_length, power_height, power_width, centered=False, combine=False)
.tag("volume")
.tag("power_box")
.edges("|X")
.fillet(power_height/3) #< XXX: cadquery doesn't allow a full 1/2 fillet
.tag("power")
)
def camera(body):
@@ -98,9 +103,10 @@ def camera(body):
.workplane()
.move(camera_min_x, camera_min_y)
.box(camera_width, camera_length, 2, centered=False, combine=False)
.tag("camera")
.tag("camera_box")
.edges("|Z")
.fillet(camera_length/3)
.tag("camera")
)
def aux(body):
@@ -120,9 +126,10 @@ def usb(body):
.workplane(offset=-10)
.move(-body_width/2, usb_min_z)
.box(usb_width, usb_height, 10, centered=(True, False, False), combine=False)
.tag("usb")
.tag("usb_box")
.edges("|Y")
.fillet(usb_height/3)
.tag("usb")
)
def PinePhone():