import cadquery as cq from cadquery import exporters import screen_pillars from modelo import ( height, mounting_pillar_positions, ti_depth, ti_radius, width, thickness as model_thickness, shell_t, ) from screen_mount import height_bottom import keyboard hinge_radius = 5.5 screw_radius = 1.5 # M3 ring_radius = 5 # M3 hinge_offset = max(p[1] for p in mounting_pillar_positions) + 6 hinge_width = 25 thickness = 43 mounting_pillar_positions = [(x, -y) for x, y in mounting_pillar_positions[:-2]] mounting_pillars = ( cq.Sketch() .push(mounting_pillar_positions) .trapezoid(-12, 12, 90, mode="a") .circle(ti_radius, mode="s") .clean() ) # This is a constant used to control how far back the hinges go # when open. It's arbitrary and can be adjusted experimentally # printing small samples hinge_slant = shell_t + 2 def model(): # Create a 2-part hinged lid model = ( cq.Workplane("XY") # Hollow box .workplane(offset=-thickness / 2) .box(width, height, thickness) .edges("|X and >Z and Z and >Y") .fillet(5) .edges("|Z") .fillet(2) .faces("X") .workplane() .center(height / 2 - hinge_offset, thickness / 2 - hinge_radius) .tag("rightSide") # Outer surface of the hinge .workplaneFromTagged("rightSide") .placeSketch(cq.Sketch().circle(hinge_radius)) .extrude(-width) # Cut middle section between the hinges .workplaneFromTagged("rightSide") .workplane(offset=-hinge_width) .placeSketch( cq.Sketch().polygon( [ (-hinge_radius, -hinge_radius), (-hinge_radius, 0), (-hinge_radius - hinge_slant, hinge_radius), (-hinge_slant, hinge_radius), (-hinge_slant, hinge_radius - hinge_slant), (hinge_radius, hinge_radius - hinge_slant), (hinge_radius, -hinge_radius), (-hinge_radius, -hinge_radius), ] ) ) .cutBlind(-width + 2 * hinge_width - 1) # Hole for screws .workplaneFromTagged("rightSide") .placeSketch(cq.Sketch().circle(screw_radius)) .cutBlind(-width) # Holes for rings & screw heads .workplaneFromTagged("rightSide") .placeSketch(cq.Sketch().circle(ring_radius)) .cutBlind(-5) .workplaneFromTagged("rightSide") .workplane(offset=-width + 4) .placeSketch(cq.Sketch().circle(ring_radius)) .cutBlind(-5) # Split hinge halves .faces(">X") .workplaneFromTagged("rightSide") .workplane(offset=-hinge_width / 2) .placeSketch(cq.Sketch().trapezoid(hinge_radius * 2 + 1, hinge_radius * 2, 90)) .cutBlind(-1) .workplaneFromTagged("rightSide") .workplane(offset=-hinge_width) .placeSketch(cq.Sketch().trapezoid(hinge_radius * 2 + 1, hinge_radius * 2, 90)) .cutBlind(-1) .workplaneFromTagged("rightSide") .workplane(offset=-width + hinge_width / 2) .placeSketch(cq.Sketch().trapezoid(hinge_radius * 2 + 1, hinge_radius * 2, 90)) .cutBlind(-1) .workplaneFromTagged("rightSide") .workplane(offset=-width + hinge_width) .placeSketch(cq.Sketch().trapezoid(hinge_radius * 2 + 1, hinge_radius * 2, 90)) .cutBlind(-1) # Threaded inserts .workplaneFromTagged("rightSide") .workplane(offset=-hinge_width / 2) .placeSketch(cq.Sketch().circle(ti_radius)) .cutBlind(-ti_depth) .workplaneFromTagged("rightSide") .workplane(offset=-width + hinge_width / 2) .placeSketch(cq.Sketch().circle(ti_radius)) .cutBlind(ti_depth) # Split two halves # First cut for the right hinge .workplaneFromTagged("rightSide") .placeSketch( cq.Sketch() .polygon( [ (0, 0), (-hinge_radius - 0.2, 0), (-hinge_radius - hinge_slant, hinge_radius), (0, hinge_radius), (0, 0), ] ) .polygon( [ (-hinge_radius - 0.2, 0), (-hinge_radius - 0.2, -1000), (-hinge_radius, -1000), (-hinge_radius, 0), (-hinge_radius - 0.2, 0), ] ) .circle(hinge_radius, mode="s") ) .cutBlind(-hinge_width / 2 - 1) # Second cut for the right hinge .workplaneFromTagged("rightSide") .workplane(offset=-hinge_width / 2) .placeSketch( cq.Sketch() .polygon( [ (0, 0), (hinge_radius + 0.2, 0), (hinge_radius + 0.2 + hinge_slant, hinge_radius), (0, hinge_radius), (0, 0), ] ) .circle(hinge_radius, mode="s") ) .cutBlind(-hinge_width / 2 - 1) # First cut for the left hinge .workplaneFromTagged("rightSide") .workplane(offset=-width + hinge_width) .placeSketch( cq.Sketch() .polygon( [ (0, 0), (hinge_radius + 0.2, 0), (hinge_radius + 0.2 + hinge_slant, hinge_radius), (0, hinge_radius), (0, 0), ] ) .circle(hinge_radius, mode="s") ) .cutBlind(-hinge_width / 2 - 1) # Second cut for the left hinge .workplaneFromTagged("rightSide") .workplane(offset=-width + hinge_width / 2) .placeSketch( cq.Sketch() .polygon( [ (0, 0), (-hinge_radius - 0.2, 0), (-hinge_radius - hinge_slant, hinge_radius), (0, hinge_radius), (0, 0), ] ) .polygon( [ (-hinge_radius - 0.2, 0), (-hinge_radius - 0.2, -1000), (-hinge_radius, -1000), (-hinge_radius, 0), (-hinge_radius - 0.2, 0), ] ) .circle(hinge_radius, mode="s") ) .cutBlind(-hinge_width / 2 - 1) ) # Cut off shape of the base model = ( model.workplaneFromTagged("rightSide") .center(-height + hinge_offset, -thickness + hinge_radius) .placeSketch( cq.Sketch().polygon( [ (0, 0), (0, keyboard.front_thickness), (shell_t, keyboard.front_thickness), (keyboard.actual_height + shell_t, keyboard.back_thickness), (keyboard.actual_height + shell_t, model_thickness), (height, model_thickness), (height, 0), (0, 0), ] ) ) .cutBlind(-1000) ) model = ( model.faces(">Z") .workplane(offset=-model_thickness) .center(-width, height) .placeSketch(mounting_pillars) .extrude(10) ) return model if __name__ == "__main__": model = model() exporters.export(model, "hinged_lid.stl") exporters.export( model, "hinged_lid.svg", opt={ "projectionDir": (0, 0, 1), "strokeWidth": 0.3, }, )