create a trivial case from the phone model
missing cutouts for ports and extrusions for buttons
This commit is contained in:
@@ -15,10 +15,15 @@ import sys
|
|||||||
|
|
||||||
sys.path.append(os.path.join(os.getcwd(), "src"))
|
sys.path.append(os.path.join(os.getcwd(), "src"))
|
||||||
|
|
||||||
|
import case
|
||||||
import pinephone
|
import pinephone
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def model():
|
||||||
|
phone = pinephone.PinePhone()
|
||||||
|
return case.case(phone)
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
logging.basicConfig()
|
logging.basicConfig()
|
||||||
logging.getLogger().setLevel(logging.INFO)
|
logging.getLogger().setLevel(logging.INFO)
|
||||||
@@ -28,14 +33,14 @@ def main():
|
|||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
model = pinephone.PinePhone()
|
model_ = model()
|
||||||
|
|
||||||
if args.export_stl:
|
if args.export_stl:
|
||||||
logger.info("exporting stl to %s", 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__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
else:
|
else:
|
||||||
result = pinephone.PinePhone()
|
result = model()
|
||||||
|
48
src/case.py
Normal file
48
src/case.py
Normal 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
|
@@ -52,19 +52,20 @@ def body():
|
|||||||
return (
|
return (
|
||||||
cq.Workplane("front")
|
cq.Workplane("front")
|
||||||
.box(body_width, body_length, body_height, centered=False)
|
.box(body_width, body_length, body_height, centered=False)
|
||||||
.tag("body")
|
.tag("body_box")
|
||||||
.faces(">>X").tag("body_right")
|
.faces(">>X").tag("body_right")
|
||||||
.faces(tag="body")
|
.faces(tag="body_box")
|
||||||
.faces("<<Y").tag("body_top")
|
.faces("<<Y").tag("body_top")
|
||||||
.faces(tag="body")
|
.faces(tag="body_box")
|
||||||
.faces(">>Y").tag("body_bottom")
|
.faces(">>Y").tag("body_bottom")
|
||||||
.faces(tag="body")
|
.faces(tag="body_box")
|
||||||
.faces(">>Z").tag("body_back")
|
.faces(">>Z").tag("body_back")
|
||||||
.faces(tag="body")
|
.faces(tag="body_box")
|
||||||
.edges("|Z")
|
.edges("|Z")
|
||||||
.fillet(body_rad_xy)
|
.fillet(body_rad_xy)
|
||||||
.edges("(|X or |Y)")
|
.edges("(|X or |Y)")
|
||||||
.fillet(body_rad_front_z)
|
.fillet(body_rad_front_z)
|
||||||
|
.tag("body")
|
||||||
)
|
)
|
||||||
|
|
||||||
def volume(body):
|
def volume(body):
|
||||||
@@ -74,9 +75,12 @@ def volume(body):
|
|||||||
.workplane()
|
.workplane()
|
||||||
.move(volume_min_y, volume_min_z)
|
.move(volume_min_y, volume_min_z)
|
||||||
.box(volume_length, volume_height, volume_width, centered=False, combine=False)
|
.box(volume_length, volume_height, volume_width, centered=False, combine=False)
|
||||||
.tag("volume")
|
.tag("volume_box")
|
||||||
.edges("|X")
|
.edges("|X")
|
||||||
.fillet(volume_height/3) #< XXX: cadquery doesn't allow a full 1/2 fillet
|
.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):
|
def power(body):
|
||||||
@@ -86,9 +90,10 @@ def power(body):
|
|||||||
.workplane()
|
.workplane()
|
||||||
.move(power_min_y, power_min_z)
|
.move(power_min_y, power_min_z)
|
||||||
.box(power_length, power_height, power_width, centered=False, combine=False)
|
.box(power_length, power_height, power_width, centered=False, combine=False)
|
||||||
.tag("volume")
|
.tag("power_box")
|
||||||
.edges("|X")
|
.edges("|X")
|
||||||
.fillet(power_height/3) #< XXX: cadquery doesn't allow a full 1/2 fillet
|
.fillet(power_height/3) #< XXX: cadquery doesn't allow a full 1/2 fillet
|
||||||
|
.tag("power")
|
||||||
)
|
)
|
||||||
|
|
||||||
def camera(body):
|
def camera(body):
|
||||||
@@ -98,9 +103,10 @@ def camera(body):
|
|||||||
.workplane()
|
.workplane()
|
||||||
.move(camera_min_x, camera_min_y)
|
.move(camera_min_x, camera_min_y)
|
||||||
.box(camera_width, camera_length, 2, centered=False, combine=False)
|
.box(camera_width, camera_length, 2, centered=False, combine=False)
|
||||||
.tag("camera")
|
.tag("camera_box")
|
||||||
.edges("|Z")
|
.edges("|Z")
|
||||||
.fillet(camera_length/3)
|
.fillet(camera_length/3)
|
||||||
|
.tag("camera")
|
||||||
)
|
)
|
||||||
|
|
||||||
def aux(body):
|
def aux(body):
|
||||||
@@ -120,9 +126,10 @@ def usb(body):
|
|||||||
.workplane(offset=-10)
|
.workplane(offset=-10)
|
||||||
.move(-body_width/2, usb_min_z)
|
.move(-body_width/2, usb_min_z)
|
||||||
.box(usb_width, usb_height, 10, centered=(True, False, False), combine=False)
|
.box(usb_width, usb_height, 10, centered=(True, False, False), combine=False)
|
||||||
.tag("usb")
|
.tag("usb_box")
|
||||||
.edges("|Y")
|
.edges("|Y")
|
||||||
.fillet(usb_height/3)
|
.fillet(usb_height/3)
|
||||||
|
.tag("usb")
|
||||||
)
|
)
|
||||||
|
|
||||||
def PinePhone():
|
def PinePhone():
|
||||||
|
Reference in New Issue
Block a user